diff options
Diffstat (limited to 'src')
9 files changed, 296 insertions, 290 deletions
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateManager.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateManager.ts index d11f05766..030c6db95 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateManager.ts +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateManager.ts @@ -20,7 +20,7 @@ export class TemplateManager { templates: Template[] = []; - fieldConditions: Record<string, Conditional[]> = {}; + conditionalFieldLogic: Record<string, Conditional[]> = {}; constructor(templateSettings: FieldSettings[]) { makeAutoObservable(this); @@ -40,16 +40,16 @@ export class TemplateManager { }; addFieldCondition = (fieldTitle: string, condition: Conditional) => { - if (this.fieldConditions[fieldTitle] === undefined) { - this.fieldConditions[fieldTitle] = [condition]; + if (this.conditionalFieldLogic[fieldTitle] === undefined) { + this.conditionalFieldLogic[fieldTitle] = [condition]; } else { - this.fieldConditions[fieldTitle].push(condition); + this.conditionalFieldLogic[fieldTitle].push(condition); } } removeFieldCondition = (fieldTitle: string, condition: Conditional) => { - if (this.fieldConditions[fieldTitle]) { - this.fieldConditions[fieldTitle] = this.fieldConditions[fieldTitle].filter(cond => cond !== condition); + if (this.conditionalFieldLogic[fieldTitle]) { + this.conditionalFieldLogic[fieldTitle] = this.conditionalFieldLogic[fieldTitle].filter(cond => cond !== condition); } } @@ -84,6 +84,8 @@ export class TemplateManager { await applyGPTContent(); + templateCopy.applyConditionalLogic(this.conditionalFieldLogic); + return templateCopy.getRenderedDoc(); }; diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.scss b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.scss index 2a1a79029..8ac718fb7 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.scss +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.scss @@ -33,6 +33,7 @@ &:hover { box-shadow: none; + background-color: rgb(60, 60, 65); } &.no-margin { @@ -57,42 +58,6 @@ margin-left: 0px; font-size: 12px; } - - &.close-menu { - font-size: 12px; - width: 18px; - height: 18px; - font-size: 12px; - margin-left: auto; - margin-right: 5px; - margin-bottom: 3px; - } - - &.options { - margin-left: 0px; - } - - &:hover { - background-color: rgb(60, 60, 65); - } - - &.top-bar { - border-bottom: 25px solid #555; - border-left: 12px solid transparent; - border-right: 12px solid transparent; - // border-top-left-radius: 5px; - // border-top-right-radius: 5px; - border-radius: 0px; - height: 0; - width: 50px; - } - - &.preview-toggle { - margin: 0px; - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - border-left: 0px; - } } .docCreatorMenu-top-buttons-container { @@ -400,30 +365,6 @@ } -.docCreatorMenu-variation-prompt-input { - display: flex; - flex-direction: row; - justify-content: flex-start; - align-items: center; - margin: 5px; - gap: 15px; - height: 30px; - width: 100%; -} - -.docCreatorMenu-variation-prompt-input-textbox { - height: 100%; - width: 80%; - color: white; - margin-top: 1%; - margin-bottom: 1%; - margin-left: 5%; - background-color: rgb(50, 50, 50); - border-radius: 5px; - overflow: hidden; - resize: none; -} - .docCreatorMenu-variations-tab { flex-grow: .5; } @@ -435,6 +376,7 @@ position: relative; flex-grow: 1; height: 100%; + width: 100%; margin: 0px; margin-top: 0px; margin-bottom: 0px; @@ -446,7 +388,7 @@ justify-content: center; align-items: center; position: absolute; - width: 100%; + width: calc(100% - 10px); bottom: 0px; margin: 0px; margin-bottom: 10px; @@ -457,11 +399,18 @@ .docCreatorMenu-templates-preview-window { display: grid; justify-content: space-evenly; + row-gap: 2rem; grid-template-columns: repeat(auto-fill, minmax(225px, 30%)); - gap: 1rem; margin: 5px; width: calc(100% - 10px); + height: 100%; padding-bottom: 40px; + + &.scrolling { + overflow-y: scroll; + max-height: 300px; + padding-bottom: 0px; + } } .divvv{ @@ -847,108 +796,6 @@ background-color: rgb(50, 50, 50); } -// .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; -// flex: 0 0 auto; - -// .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-type-selection-container { -// display: flex; -// flex-direction: row; -// align-items: center; -// background-color: rgb(40, 40, 40); -// border: 1px solid rgb(100, 100, 100); -// border-radius: 3px; -// width: 31%; -// height: 25px; -// padding-left: 3px; -// color: whitesmoke; - -// .placeholder { -// color: gray; -// } - -// &:hover .placeholder { -// display: none; -// } - -// .bubbles { -// display: none; -// } - -// .text { -// margin-top: 5px; -// margin-bottom: 5px; -// } - -// &:hover .bubbles { -// display: flex; -// flex-direction: row; -// align-items: flex-start; -// } - -// &:hover .type-display { -// display: none; -// } - -// .bubble { -// margin: 5px; -// } - -// &:hover .bubble { -// margin-top: 7px; -// } -// } -// } - -// .field-description-container { -// background-color: rgb(40, 40, 40); -// border: 1px solid rgb(100, 100, 100); -// border-radius: 3px; -// width: 100%; -// height: 100%; -// resize: none; - -// ::-webkit-scrollbar-track { -// background: none; -// } -// } - -// .top-right { -// position: absolute; -// top: 0px; -// right: 0px; -// } -// } -// } - .field-panel { display: flex; flex-direction: column; @@ -1214,4 +1061,127 @@ } } -}
\ No newline at end of file +} + +//------------------------------------------------------------------------------------------------------------------------------------------ +// EditingWindow CSS +//-------------------------------------------------------------------------------------------------------------------------------------------- + +.docCreatorMenu-editing-firefly-section { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + width: 100%; + padding: 5px; +} + +.docCreatorMenu-firefly-options { + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-end; + height: fit-content; + width: 100%; +} + +.docCreatorMenu-variation-prompt-row { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + gap: 15px; + height: fit-content; + width: 100%; +} + +.docCreatorMenu-variation-prompt-input-textbox { + height: 40px; + width: 80%; + color: white; + margin-top: 1%; + margin-bottom: 1%; + margin-left: 5%; + background-color: rgb(50, 50, 50); + border-radius: 5px; + overflow: hidden; + resize: none; +} + +.options‑menu { + display: flex; + align-items: center; + justify-content: center; + gap: 2rem; + padding: 0.5rem 1rem; + background: rgb(50, 50, 50); + color: whitesmoke; + font-family: system-ui, sans-serif; + font-size: 0.9rem; + flex-wrap: wrap; + } + + .menu‑item { + display: flex; + align-items: center; + gap: 0.5rem; + white-space: nowrap; + } + + .menu‑item input[type="range"] { + width: 7rem; + accent-color: whitesmoke; + } + + .value { + min-width: 2ch; + text-align: right; + } + + .switch { + gap: 0.75rem; + } + + .switch .slider { + position: relative; + width: 2.2rem; + height: 1.1rem; + background: whitesmoke; + border-radius: 1rem; + cursor: pointer; + transition: background 0.2s; + } + + .switch .slider::before { + content: ''; + position: absolute; + top: 0.1rem; + left: 0.1rem; + width: 0.9rem; + height: 0.9rem; + background: rgb(50, 50, 50); + border-radius: 50%; + transition: transform 0.2s; + } + + .switch input { + display: none; + } + + .switch input:checked + .slider { + background: #9fe29d; + } + + .switch input:checked + .slider::before { + transform: translateX(1.1rem); + } + +.firefly-option-label { + padding: .2em .6em .3em; + font-size: 100%; + color: whitesmoke; + text-align: center; +} + + diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx index 2e4b81253..2565a9332 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx @@ -36,7 +36,7 @@ import { CgPathIntersect } from 'react-icons/cg'; import { StaticContentField } from './TemplateFieldTypes/StaticContentField'; import { TemplateMenuAIUtils } from './Backend/TemplateMenuAIUtils' import { TemplatePreviewGrid } from './Menu/TemplatePreviewGrid'; -import { TemplateEditingWindow } from './Menu/TemplateEditingWindow'; +import { FireflyStructureOptions, TemplateEditingWindow } from './Menu/TemplateEditingWindow'; import { DocCreatorMenuButton } from './Menu/DocCreatorMenuButton'; export enum LayoutType { @@ -103,7 +103,6 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> @observable _userCreatedFields: Col[] = []; @observable _collapsedCols: String[] = []; //any columns whose options panels are hidden - @observable _conditions: Conditional[] = []; @observable _currEditingConditional: Conditional = {} as Conditional; @observable _layout: { type: LayoutType; yMargin: number; xMargin: number; columns?: number; repeat: number } = { type: LayoutType.FREEFORM, yMargin: 10, xMargin: 10, columns: 3, repeat: 0 }; @@ -594,7 +593,8 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> ); }; - generateVariations = async (onDoc: Doc, prompt: string): Promise<string[]> => { + generateVariations = async (onDoc: Doc, prompt: string, options: FireflyStructureOptions): Promise<string[]> => { + const { numVariations, temperature, useStyleRef } = options; this.variations = []; const mainCollection = this._dataViz?.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; @@ -603,12 +603,12 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> clone.x = 10000; clone.y = 10000; - await DrawingFillHandler.drawingToImage(clone, 100, prompt, clone, this) + await DrawingFillHandler.drawingToImage(clone, 100 - temperature, prompt, useStyleRef ? clone : undefined, this, numVariations) return this.variations; } - @observable variations: string[] = [] + variations: string[] = [] @action addVariation = (url: string) => { this.variations.push(url); @@ -918,7 +918,7 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> } get dashboardContents() { - const conditionForm = (title: string, parameters?: Conditional) => { + const conditionForm = (title: string, parameters?: Conditional, empty: boolean = false) => { const params: Conditional = parameters ?? this._currEditingConditional; @@ -942,8 +942,8 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> <div className='form-row-plain-text'>then</div> <div className="operator-options-dropdown"> <span className="operator-dropdown-current">{params.target ?? 'self'}</span> - <div className='operator-dropdown-option' onPointerDown={() => {params.target = 'self'}}>{'self'}</div> - <div className='operator-dropdown-option' onPointerDown={() => {params.target = 'template'}}>{'template'}</div> + <div className='operator-dropdown-option' onPointerDown={() => {params.target = 'self'}}>{'own'}</div> + <div className='operator-dropdown-option' onPointerDown={() => {params.target = 'template'}}>{`template's`}</div> </div> <textarea className="form-row-textarea" @@ -959,17 +959,11 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> defaultValue={params.value} /> </div> - <div className='form-action-button' onPointerDown={e => this.setUpButtonClick(e, runInAction(() => () => { - if (this._currEditingConditional === params) { - params.field = title; - this._conditions.push(params); - this._currEditingConditional = {} as Conditional; - } else { - this._conditions = this._conditions.filter(cond => cond !== params); - } - }))}> - <FontAwesomeIcon icon={this._currEditingConditional === params ? 'plus' : 'minus'} color='white'/> - </div> + {empty ? + <DocCreatorMenuButton icon={'plus'} styles={'float-right'} function={() => this.templateManager.addFieldCondition(title, params)}/> + : + <DocCreatorMenuButton icon={'minus'} styles={'float-right'} function={() => this.templateManager.removeFieldCondition(title, params)}/> + } </div> ) } @@ -1030,8 +1024,8 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> </div> <div className="conditionals-section"> <span className="conditionals-title">Conditional Logic</span> - {conditionForm(field.title, this._currEditingConditional)} - {this._conditions.map(condition => conditionForm(condition.field, condition))} + {conditionForm(field.title, this._currEditingConditional, true)} + {this.templateManager.conditionalFieldLogic[field.title]?.map(condition => conditionForm(condition.field, condition))} </div> </> } diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx index 0434d0ccb..4ff509d73 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, observable, reaction, runInAction } from "mobx"; +import { action, makeAutoObservable, makeObservable, observable, reaction, runInAction } from "mobx"; import React from "react"; import { returnFalse, returnEmptyFilter, returnTrue } from "../../../../../../ClientUtils"; import { emptyFunction } from "../../../../../../Utils"; @@ -16,93 +16,77 @@ import { ObservableReactComponent } from "../../../../ObservableReactComponent"; import { IDisposer } from "mobx-utils"; import { ImageField } from "../../../../../../fields/URLField"; import { DocCreatorMenuButton } from "./DocCreatorMenuButton"; +import { TbHistory } from "react-icons/tb"; -interface TemplateEditingWindowProps { +export type FireflyStructureOptions = { + numVariations: number; + temperature: number; + useStyleRef: boolean; +} + +interface FireflyVariationsTabProps { menu: DocCreatorMenu; template: Template; } @observer -export class TemplateEditingWindow extends ObservableReactComponent<TemplateEditingWindowProps> { +export class FireflyVariationsTab extends ObservableReactComponent<FireflyVariationsTabProps> { - private fireflyPrompt: string = 'Use this template to generate an empty baseball card template.'; - private previewWindow: HTMLDivElement | null = null; - private disposers: { [name: string]: IDisposer } = {}; - private promptInput: HTMLTextAreaElement | null = null; + private prompt: string = 'Use this template to generate an empty baseball card template.'; + + @observable private promptInput: HTMLTextAreaElement | null = null; @observable _loading: boolean = false; @observable _variationsTabOpen: boolean = false; @observable _variationURLs: string[] = []; - @observable _variations: Template[] = []; - - componentDidMount(): void { - this.disposers.windowDimensions = reaction(() => - this._props.menu._resizing, - () => { this.forceUpdate() }, - { fireImmediately: true } - ); - } - - componentWillUnmount() { - Object.values(this.disposers).forEach(disposer => disposer?.()); - } - - setPromptInputRef: React.LegacyRef<HTMLTextAreaElement> = (node) => { - this.promptInput = node; - this.forceUpdate(); - } - setContainerRef: React.LegacyRef<HTMLDivElement> = (node) => { - this.previewWindow = node; - this.forceUpdate(); - } + @observable private fireflyOptions: FireflyStructureOptions = {numVariations: 3, temperature: 0, useStyleRef: false}; - @action setVariationTab = (open: boolean) => { - this._variationsTabOpen = open; - if (this.previewWindow && open) { - this.previewWindow.style.height = String(Number(this.previewWindow.clientHeight) * .6); - } else if (this.previewWindow && !open) { - this.previewWindow.style.height = String(Number(this.previewWindow.clientHeight) * 5/3); - } - this.forceUpdate(); + constructor(props: FireflyVariationsTabProps) { + super(props); + makeObservable(this); } generateVariations = async () => { this._props.menu._variations = []; this._loading = true; - this._variationURLs = await this._props.menu.generateVariations(this._props.template.clone(false).getRenderedDoc()!, this.fireflyPrompt); + const doc: Doc = this._props.template.clone(false).getRenderedDoc()!; + this._variationURLs = await this._props.menu.generateVariations(doc, this.prompt, this.fireflyOptions); this._variationURLs.forEach(url => { const newTemplate: Template = this._props.template.clone(true); this._props.menu._variations.push(newTemplate); }); - this._loading = false; setTimeout(() => { this._variationURLs.forEach((url, i) => { this._props.menu._variations[i].setImageAsBackground(url, true); }); - this.forceUpdate(); + this._loading = false; }); } - get fireflyVariationsTab() { + setPromptInputRef: React.LegacyRef<HTMLTextAreaElement> = (node) => { + this.promptInput = node; + } + render() { return ( - <> + <div className='docCreatorMenu-editing-firefly-section'> <div className="docCreatorMenu-option-divider full no-margin-bottom"/> <TemplatePreviewGrid menu={this._props.menu} title={'Generate Variations'} loading={this._loading} + styles={'scrolling'} templates={this._props.menu._variations} optionsButtonOpts={['gear', () => {}]} previewBoxRightButtonOpts={['gear', (template: Template) => {this.forceUpdate();}]} /> - <div className="docCreatorMenu-section"> - <div className="docCreatorMenu-variation-prompt-input"> + <div className="docCreatorMenu-firefly-options"> + <div className="docCreatorMenu-variation-prompt-row"> <textarea className="docCreatorMenu-variation-prompt-input-textbox" ref={this.setPromptInputRef} - onChange={e => this.fireflyPrompt = e.target.value} + onChange={e => this.prompt = e.target.value} onInput={() => { if (this.promptInput !== null) { this.promptInput.style.height = 'auto'; @@ -114,13 +98,84 @@ export class TemplateEditingWindow extends ObservableReactComponent<TemplateEdit /> <DocCreatorMenuButton icon={'arrows-rotate'} styles={'border'} function={this.generateVariations}/> </div> - <div className='docCreatorMenu-variation-options-container'> - - </div> + <nav className="options‑menu"> + <div className="menu‑item switch"> + <input type="checkbox" checked={this.fireflyOptions.useStyleRef} + onChange={(e) => runInAction(() => this.fireflyOptions.useStyleRef = e.target.checked)} + /> + <span className="slider round"></span> + <span className="firefly-option-label">Use template as style guide</span> + </div> + <div className="menu‑item"> + <span className="firefly-option-label">Variations</span> + <input type="range" id="variations" + min="1" + max="5" + value={this.fireflyOptions.numVariations} + onChange={(e) => runInAction(() => this.fireflyOptions.numVariations = Number(e.target.value))} + /> + <span className="value" id="varVal">{this.fireflyOptions.numVariations}</span> + </div> + <div className="menu‑item"> + <span className="firefly-option-label">Temperature</span> + <input type="range" id="temperature" + min="1" + max="100" + value={this.fireflyOptions.temperature} + onChange={(e) => runInAction(() => this.fireflyOptions.temperature = Number(e.target.value))} + /> + <span className="value" id="tempVal">{this.fireflyOptions.temperature}</span> + </div> + </nav> </div> - </> + </div> ) } +} + +interface TemplateEditingWindowProps { + menu: DocCreatorMenu; + template: Template; +} + +@observer +export class TemplateEditingWindow extends ObservableReactComponent<TemplateEditingWindowProps> { + + private disposers: { [name: string]: IDisposer } = {}; + + @observable private previewWindow: HTMLDivElement | null = null; + + @observable _variationsTabOpen: boolean = false; + + constructor(props: TemplateEditingWindowProps) { + super(props); + makeObservable(this); + } + + componentDidMount(): void { + this.disposers.windowDimensions = reaction(() => + this._props.menu._resizing, + () => { this.forceUpdate() }, + { fireImmediately: true } + ); + } + + componentWillUnmount() { + Object.values(this.disposers).forEach(disposer => disposer?.()); + } + + setContainerRef: React.LegacyRef<HTMLDivElement> = (node) => { + this.previewWindow = node; + } + + @action setVariationTab = (open: boolean) => { + this._variationsTabOpen = open; + if (this.previewWindow && open) { + this.previewWindow.style.height = String(Number(this.previewWindow.clientHeight) * .6); + } else if (this.previewWindow && !open) { + this.previewWindow.style.height = String(Number(this.previewWindow.clientHeight) * 5/3); + } + } get renderedDocPreview(){ const doc: Doc = this._props.template.getRenderedDoc() as unknown as Doc; @@ -158,7 +213,11 @@ export class TemplateEditingWindow extends ObservableReactComponent<TemplateEdit <div className="docCreatorMenu-expanded-template-preview"> <div className="top-panel"/> {this.renderedDocPreview} - {this._variationsTabOpen ? this.fireflyVariationsTab : null} + {this._variationsTabOpen ? <FireflyVariationsTab + menu={this._props.menu} + template={this._props.template} + /> + : null} <div className="right-buttons-panel"> <DocCreatorMenuButton icon={'minimize'} function={() => { if (this._props.template === this._props.menu._selectedTemplate) { @@ -167,6 +226,7 @@ export class TemplateEditingWindow extends ObservableReactComponent<TemplateEdit this._props.menu.setExpandedView(undefined); }}/> <DocCreatorMenuButton icon={'lightbulb'} function={() => this.setVariationTab(!this._variationsTabOpen)}/> + <DocCreatorMenuButton icon={'arrow-rotate-backward'} function={() => { this._props.menu.setExpandedView(this._props.template); this.forceUpdate(); }}/> </div> </div> </div> diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewBox.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewBox.tsx index c9f817d2f..de2f9e455 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewBox.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewBox.tsx @@ -1,7 +1,7 @@ import { Colors } from "@dash/components/src"; import { FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import { Template } from "../Template"; -import { reaction, runInAction } from "mobx"; +import { makeObservable, observable, reaction, runInAction } from "mobx"; import React from "react"; import { ObservableReactComponent } from "../../../../ObservableReactComponent"; import { DocCreatorMenu } from "../DocCreatorMenu"; @@ -17,7 +17,7 @@ import { ImageField } from "../../../../../../fields/URLField"; import { ImageCast } from "../../../../../../fields/Types"; import { observer } from "mobx-react"; -export interface TemplatePreviewBox_props { +export interface TemplatePreviewBoxProps { template: Template; menu: DocCreatorMenu; leftButtonOpts?: [icon: IconProp, func: (...args: any) => void] @@ -25,32 +25,18 @@ export interface TemplatePreviewBox_props { } @observer -export class TemplatePreviewBox extends ObservableReactComponent<TemplatePreviewBox_props> { +export class TemplatePreviewBox extends ObservableReactComponent<TemplatePreviewBoxProps> { - private previewWindow: HTMLDivElement | null = null; - // private icon: ImageField | undefined = undefined; + @observable private previewWindow: HTMLDivElement | null = null; setContainerRef: React.LegacyRef<HTMLDivElement> = (node) => { this.previewWindow = node; - this.forceUpdate(); } - // componentDidMount(): void { - // console.log('mounted') - // setTimeout(() => { - // const docView = DocumentView.getDocumentView(this.doc); - // if (docView) { - // console.log('docview found') - // docView.ComponentView?.updateIcon?.(); - // setTimeout(() => { - // console.log('componentview found: ', docView.ComponentView) - // this.icon = ImageCast(docView.Document.icon); - // console.log('icon is:', this.icon, ' from: ', docView.Document.icon); - // this.forceUpdate(); - // }, 5000); - // } - // }, 1000); - // } + constructor(props: TemplatePreviewBoxProps) { + super(props); + makeObservable(this); + } get doc() { return this._props.template.getRenderedDoc() as Doc; @@ -83,8 +69,6 @@ export class TemplatePreviewBox extends ObservableReactComponent<TemplatePreview <button className="option-button right" onPointerDown={e => this._props.menu.setUpButtonClick(e, () => this._props.rightButtonOpts)}> <FontAwesomeIcon icon={this._props.rightButtonOpts![0]} color="white" /> </button> : null } - {/* { this.icon ? - <img className="docCreatorMenu-preview-image" src={this.icon.url.href.replace('.png', '_o.png')} /> */} <DocumentView Document={this.doc} isContentActive={emptyFunction} // !!! should be return false diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewGrid.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewGrid.tsx index 84ca6546d..d53853c52 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewGrid.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewGrid.tsx @@ -1,6 +1,6 @@ import { Colors } from "@dash/components/src"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, observable, runInAction } from "mobx"; +import { action, makeObservable, observable, runInAction } from "mobx"; import React from "react"; import ReactLoading from "react-loading"; import { Doc } from "../../../../../../fields/Doc"; @@ -18,6 +18,7 @@ export interface SuggestedTemplatesProps { loading?: boolean; templates: Template[]; title: string; + styles?: string; optionsButtonOpts?: [IconProp, (...args: any) => any]; previewBoxLeftButtonOpts?: [IconProp, (...args: any) => any]; previewBoxRightButtonOpts?: [IconProp, (...args: any) => any]; @@ -26,6 +27,11 @@ export interface SuggestedTemplatesProps { @observer export class TemplatePreviewGrid extends ObservableReactComponent<SuggestedTemplatesProps> { + constructor(props: SuggestedTemplatesProps) { + super(props); + makeObservable(this); + } + render() { return ( <div className="docCreatorMenu-section"> @@ -35,7 +41,7 @@ export class TemplatePreviewGrid extends ObservableReactComponent<SuggestedTempl <DocCreatorMenuButton icon={this._props.optionsButtonOpts[0] as IconProp} styles={'float-right'} function={() => runInAction(this._props.optionsButtonOpts![1])}/> : null} </div> - <div className="docCreatorMenu-templates-preview-window"> + <div className={"docCreatorMenu-templates-preview-window " + this._props.styles}> {this._props.loading ? (<div className="loading-spinner"> <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} /> diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts index 7ff521f18..fae0d06e4 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts @@ -10,8 +10,6 @@ import { Doc } from '../../../../../fields/Doc'; export class Template { _mainField: DynamicField; - private conditionalLogic: Record<string, Conditional[]> = {}; - /** * A Template can be created from a description of its fields (FieldSettings) or from a DynamicField * @param definition definition of template as settings or DynamicField @@ -84,11 +82,11 @@ export class Template { return maxMatches === this.contentFields.length && this.title !== 'template_framework'; }; - applyConditionalLogicToField = (field: TemplateField) => { + applyConditionalLogicToField = (field: TemplateField, logic: Record<string, Conditional[]>) => { if (field instanceof DynamicField) return; - const logic: Conditional[] = this.conditionalLogic[field.getTitle()]; + const fieldStatements: Conditional[] = logic[field.getTitle()]; const content = field.getContent() - logic && logic.forEach(statement => { + fieldStatements && fieldStatements.forEach(statement => { if (content === statement.condition) { if (statement.target === 'template') { this._mainField.renderedDoc![statement.attribute] = statement.value; @@ -99,17 +97,10 @@ export class Template { }) } - applyConditionalLogic = () => { - const fields: TemplateField[] = [this._mainField, ...this.allFields]; - fields.forEach(this.applyConditionalLogicToField); - } - - addConditionalStatement = (field: string, statement: Conditional) => { - !this.conditionalLogic[field] ? this.conditionalLogic[field] = [statement] : this.conditionalLogic[field].push(statement); - } - - removeConditionalStatement = (field: string, statement: Conditional) => { - this.conditionalLogic[field] = this.conditionalLogic[field]?.filter(cond => cond !== statement); + applyConditionalLogic = (logic: Record<string, Conditional[]>) => { + console.log('applying logic: ', logic) + const fields: TemplateField[] = [...this.allFields]; + fields.forEach(field => this.applyConditionalLogicToField(field, logic)); } setImageAsBackground(url: string, makeTransparent: boolean = false) { diff --git a/src/client/views/smartdraw/DrawingFillHandler.tsx b/src/client/views/smartdraw/DrawingFillHandler.tsx index ad5ef7118..48107ff0d 100644 --- a/src/client/views/smartdraw/DrawingFillHandler.tsx +++ b/src/client/views/smartdraw/DrawingFillHandler.tsx @@ -15,7 +15,7 @@ import { AspectRatioLimits, FireflyDimensionsMap, FireflyImageDimensions, Firefl const DashDropboxId = '2m86iveqdr9vzsa'; export class DrawingFillHandler { - static drawingToImage = async (drawing: Doc, strength: number, user_prompt: string, styleDoc?: Doc, fromDocCreator?: DocCreatorMenu) => { + static drawingToImage = async (drawing: Doc, strength: number, user_prompt: string, styleDoc?: Doc, fromDocCreator?: DocCreatorMenu, numVariations: number = 4) => { const docData = drawing[DocData]; const tags = StrListCast(docData.tags).map(tag => tag.slice(1)); const styles = tags.filter(tag => FireflyStylePresets.has(tag)); @@ -45,9 +45,8 @@ export class DrawingFillHandler { return imageUrlToBase64(structureUrl) .then(gptDescribeImage) .then((prompt, newPrompt = user_prompt || prompt) => - Networking.PostToServer('/queryFireflyImageFromStructure', { prompt: `${newPrompt}`, width: dims.width, height: dims.height, structureUrl, strength, presets: styles, styleUrl }) + Networking.PostToServer('/queryFireflyImageFromStructure', { prompt: `${newPrompt}`, width: dims.width, height: dims.height, structureUrl, strength, presets: styles, styleUrl, variations: numVariations}) .then(res => { - console.log('res is: ', res) const genratedDocs = DocCast(drawing.ai_firefly_generatedDocs) ?? Docs.Create.MasonryDocument([], { _width: 400, _height: 400 }); drawing[DocData].ai_firefly_generatedDocs = genratedDocs; (res as Upload.ImageInformation[]).map(info => { diff --git a/src/server/ApiManagers/FireflyManager.ts b/src/server/ApiManagers/FireflyManager.ts index e5e030b0f..a272494b8 100644 --- a/src/server/ApiManagers/FireflyManager.ts +++ b/src/server/ApiManagers/FireflyManager.ts @@ -22,7 +22,7 @@ export default class FireflyManager extends ApiManager { return undefined; }); - generateImageFromStructure = (prompt: string = 'a realistic illustration of a cat coding', width: number = 2048, height: number = 2048, structureUrl: string, strength: number = 50, styles: string[], styleUrl: string | undefined) => + generateImageFromStructure = (prompt: string = 'a realistic illustration of a cat coding', width: number = 2048, height: number = 2048, structureUrl: string, strength: number = 50, styles: string[], styleUrl: string | undefined, variations: number = 4) => this.getBearerToken().then(response => response?.json().then((data: { access_token: string }) => //prettier-ignore @@ -36,7 +36,7 @@ export default class FireflyManager extends ApiManager { ], body: JSON.stringify({ prompt, - numVariations: 4, + numVariations: variations, detailLevel: 'preview', modelVersion: 'image3_fast', size: { width, height }, @@ -308,7 +308,7 @@ export default class FireflyManager extends ApiManager { return { styleUrl, structureUrl: dropboxStructureUrl }; }) .then(uploads => - this.generateImageFromStructure(req.body.prompt, req.body.width, req.body.height, uploads.structureUrl, req.body.strength, req.body.presets, uploads.styleUrl) + this.generateImageFromStructure(req.body.prompt, req.body.width, req.body.height, uploads.structureUrl, req.body.strength, req.body.presets, uploads.styleUrl, req.body.variations) .then(images => { Promise.all((images ?? [new Error('no images were generated')]).map(fire => (fire instanceof Error ? fire : DashUploadUtils.UploadImage(fire.url)))) .then(dashImages => { |