aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateManager.ts12
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuAIUtils.ts129
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuFireflyManager.ts0
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuGPTManager.ts0
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx261
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx134
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewBox.tsx31
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatesSidescrollDisplay.tsx (renamed from src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/SuggestedTemplatesWindow.tsx)33
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts15
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/TemplateField.ts21
10 files changed, 423 insertions, 213 deletions
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateManager.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateManager.ts
index 6d63078a8..ef7dbc7ab 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateManager.ts
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateManager.ts
@@ -2,10 +2,10 @@ import { action, makeAutoObservable } from 'mobx';
import { Col } from '../DocCreatorMenu';
import { FieldSettings } from '../TemplateFieldTypes/TemplateField';
import { Template } from '../Template';
-import { NumListCast } from '../../../../../../fields/Doc';
+import { Doc, NumListCast } from '../../../../../../fields/Doc';
import { DataVizBox } from '../../DataVizBox';
import { TemplateFieldType } from '../TemplateBackend';
-import { TemplateMenuGPTManager } from './TemplateMenuGPTManager';
+import { TemplateMenuAIUtils } from './TemplateMenuAIUtils';
export type Conditional = {
field: string;
@@ -28,7 +28,7 @@ export class TemplateManager {
}
initializeTemplates = (templateSettings: FieldSettings[]) => templateSettings.map(settings => {
- return new Template(settings, this.fieldConditions)});
+ return new Template(settings)});
getValidTemplates = (cols: Col[]) => this.templates.filter(template => template.isValidTemplate(cols));
@@ -53,7 +53,7 @@ export class TemplateManager {
}
}
- createDocsFromTemplate = action((dv: DataVizBox, template: Template, csvColumns: Col[], GPTManager: TemplateMenuGPTManager, debug: boolean = false) => {
+ createDocsFromTemplate = action((dv: DataVizBox, template: Template, csvColumns: Col[], debug: boolean = false) => {
const fields = Array.from(Object.keys(dv.records[0]));
const processContent = (content: { [title: string]: string }) => {
@@ -66,7 +66,7 @@ export class TemplateManager {
field && field.setContent(content[title], field.viewType);
});
- const gptFunc = (type: TemplateFieldType) => (type === TemplateFieldType.VISUAL ? GPTManager.renderGPTImageCall : GPTManager.renderGPTTextCall);
+ const gptFunc = (type: TemplateFieldType) => (type === TemplateFieldType.VISUAL ? TemplateMenuAIUtils.renderGPTImageCall : TemplateMenuAIUtils.renderGPTTextCall);
const gptPromises = csvColumns
.filter(field => field.type !== TemplateFieldType.UNSET && field.AIGenerated)
.map(field => {
@@ -76,7 +76,7 @@ export class TemplateManager {
}
});
- return Promise.all(gptPromises)
+ return templateCopy.getRenderedDoc();
};
const rowContents = debug
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuAIUtils.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuAIUtils.ts
new file mode 100644
index 000000000..446fe3442
--- /dev/null
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuAIUtils.ts
@@ -0,0 +1,129 @@
+import { action } from "mobx";
+import { Upload } from "openai/resources";
+import { ClientUtils } from "../../../../../../ClientUtils";
+import { Networking } from "../../../../../Network";
+import { gptImageCall, gptAPICall, GPTCallType } from "../../../../../apis/gpt/GPT";
+import { Col } from "../DocCreatorMenu";
+import { TemplateFieldSize, TemplateFieldType } from "../TemplateBackend";
+import { TemplateField, ViewType } from "../TemplateFieldTypes/TemplateField";
+import { Template } from "../Template";
+import { Doc } from "../../../../../../fields/Doc";
+import { DrawingFillHandler } from "../../../../smartdraw/DrawingFillHandler";
+import { CollectionFreeFormView } from "../../../../collections/collectionFreeForm";
+
+export class TemplateMenuAIUtils {
+
+ public static generateGPTImage = async (prompt: string): Promise<string | undefined> => {
+ try {
+ const res = await gptImageCall(prompt);
+
+ if (res) {
+ const result = (await Networking.PostToServer('/uploadRemoteImage', { sources: res })) as Upload.FileInformation[];
+ const source = ClientUtils.prepend(result[0].accessPaths.agnostic.client);
+ return source;
+ }
+ } catch (e) {
+ console.log(e);
+ }
+ };
+
+ public static renderGPTImageCall = async (template: Template, col: Col, fieldNumber: number | undefined): Promise<boolean> => {
+ const generateAndLoadImage = async (fieldNum: string, column: Col, prompt: string) => {
+ const url = await this.generateGPTImage(prompt);
+ const field: TemplateField = template.getFieldByID(Number(fieldNum));
+
+ field.setContent(url ?? '', ViewType.IMG);
+ field.setTitle(col.title);
+ };
+
+ const fieldContent: string = template.compiledContent;
+
+ try {
+ 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. Your prompt should focus heavily on visual elements to help the image generator; avoid unecessary info that might distract it. 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);
+
+ await generateAndLoadImage(String(fieldNumber), col, prompt);
+ } catch (e) {
+ console.log(e);
+ }
+ return true;
+ };
+
+ public static renderGPTTextCall = async (template: Template, col: Col, fieldNum: number | undefined): Promise<boolean> => {
+ const wordLimit = (size: TemplateFieldSize) => {
+ switch (size) {
+ case TemplateFieldSize.TINY:
+ return 2;
+ case TemplateFieldSize.SMALL:
+ return 5;
+ case TemplateFieldSize.MEDIUM:
+ return 20;
+ case TemplateFieldSize.LARGE:
+ return 50;
+ case TemplateFieldSize.HUGE:
+ return 100;
+ default:
+ return 10;
+ }
+ };
+
+ const textAssignment = `--- title: ${col.title}, prompt: ${col.desc}, word limit: ${wordLimit(col.sizes[0])} words, assigned field: ${fieldNum} ---`;
+
+ const fieldContent: string = template.compiledContent;
+
+ try {
+ const prompt = fieldContent + textAssignment;
+
+ const res = await gptAPICall(`${Math.random() * 100000}: ${prompt}`, GPTCallType.FILL);
+
+ if (res) {
+ const assignments: { [title: string]: { number: string; content: string } } = JSON.parse(res);
+ Object.entries(assignments).forEach(([, /* title */ info]) => {
+ const field: TemplateField = template.getFieldByID(Number(info.number));
+
+ field.setContent(info.content ?? '', ViewType.TEXT);
+ field.setTitle(col.title);
+ });
+ }
+ } catch (err) {
+ console.log(err);
+ }
+
+ return true;
+ };
+
+ /**
+ * Populates a preset template framework with content from a datavizbox or any AI-generated content.
+ * @param template the preloaded template framework being filled in
+ * @param assignments a list of template field numbers (from top to bottom) and their assigned columns from the linked dataviz
+ * @returns a doc containing the fully rendered template
+ */
+ public static applyGPTContentToTemplate = async (template: Template, assignments: { [field: string]: Col }): Promise<Template | undefined> => {
+ const GPTTextCalls = Object.entries(assignments).filter(([, col]) => col.type === TemplateFieldType.TEXT && !col.AIGenerated);
+ const GPTIMGCalls = Object.entries(assignments).filter(([, col]) => col.type === TemplateFieldType.VISUAL && !col.AIGenerated);
+
+ if (GPTTextCalls.length) {
+ const promises = GPTTextCalls.map(([str, col]) => {
+ return TemplateMenuAIUtils.renderGPTTextCall(template, col, Number(str));
+ });
+
+ await Promise.all(promises);
+ }
+
+ if (GPTIMGCalls.length) {
+ const promises = GPTIMGCalls.map(async ([fieldNum, col]) => {
+ return TemplateMenuAIUtils.renderGPTImageCall(template, col, Number(fieldNum));
+ });
+
+ await Promise.all(promises);
+ }
+
+ return template;
+ };
+
+} \ No newline at end of file
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuFireflyManager.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuFireflyManager.ts
deleted file mode 100644
index e69de29bb..000000000
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuFireflyManager.ts
+++ /dev/null
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuGPTManager.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuGPTManager.ts
deleted file mode 100644
index e69de29bb..000000000
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuGPTManager.ts
+++ /dev/null
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx
index ed2e20843..48fea91e2 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx
@@ -34,8 +34,9 @@ import { TemplateManager } from './Backend/TemplateManager';
import { DrawingFillHandler } from '../../../smartdraw/DrawingFillHandler';
import { CgPathIntersect } from 'react-icons/cg';
import { StaticContentField } from './TemplateFieldTypes/StaticContentField';
-import { SuggestedTemplatesWindow } from './Menu/SuggestedTemplatesWindow';
-import { TemplateMenuGPTManager } from './Backend/TemplateMenuGPTManager';
+import { TemplateMenuAIUtils } from './Backend/TemplateMenuAIUtils'
+import { TemplateSidescrollView } from './Menu/TemplatesSidescrollDisplay';
+import { TemplateEditingWindow } from './Menu/TemplateEditingWindow';
export enum LayoutType {
FREEFORM = 'Freeform',
@@ -91,7 +92,6 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps>
private _disposers: { [name: string]: IDisposer } = {};
private _ref: HTMLDivElement | null = null;
private templateManager: TemplateManager;
- private GPTManager: TemplateMenuGPTManager;
@observable _fullyRenderedDocs: Doc[] = []; // collection of templates filled in with content
@observable _renderedDocCollection: Doc | undefined = undefined; // fullyRenderedDocs in a parent collection
@@ -102,27 +102,16 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps>
@observable _currEditingTemplate: Template | undefined = undefined;
@observable _userCreatedFields: Col[] = [];
- @observable _selectedCols: { title: string; type: string; desc: string }[] | undefined = [];
@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 };
@observable _savedLayouts: DataVizTemplateLayout[] = [];
- @observable _expandedPreview: Doc | undefined = undefined;
- @observable _variationsTab: boolean = false;
- @observable _numVarsToGenerate: number = 3;
@observable _loadingVariants: boolean = false;
- @observable _currentVariations: Doc[] = [];
- @observable _variationPrompt: string = 'Use this template to generate an empty baseball card template.';
- _previewWindow: HTMLDivElement | null = null;
@observable _suggestedTemplates: Template[] = [];
- @observable _suggestedTemplatePreviews: { doc: Doc; template: Template }[] = [];
- @observable _GPTOpt: boolean = false;
- @observable _callCount: number = 0;
@observable _GPTLoading: boolean = false;
- @observable _DOCCC: Doc | undefined;
@observable _pageX: number = 0;
@observable _pageY: number = 0;
@@ -133,9 +122,8 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps>
@observable _startPos?: { x: number; y: number };
@observable _shouldDisplay: boolean = false;
- @observable _menuContent: 'templates' | 'options' | 'saved' | 'dashboard' = 'templates';
+ @observable _menuContent: 'templates' | 'options' | 'saved' | 'dashboard' | 'templateEditing' = 'templates';
@observable _dragging: boolean = false;
- @observable _draggingIndicator: boolean = false;
@observable _dataViz?: DataVizBox;
@observable _interactionLock: boolean | undefined;
@observable _snapPt: { x: number; y: number } = { x: 0, y: 0 };
@@ -145,20 +133,14 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps>
@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: 400, height: 400 };
- @observable _editing: boolean = false;
constructor(props: DocCreateMenuProps) {
super(props);
makeObservable(this);
DocCreatorMenu.Instance = this;
this.templateManager = new TemplateManager(TemplateLayouts.allTemplates);
- this.GPTManager = new TemplateMenuGPTManager();
}
- setContainerRef: React.LegacyRef<HTMLDivElement> = (node) => {
- this._previewWindow = node;
- };
-
@action setDataViz = (dataViz: DataVizBox) => {
this._dataViz = dataViz;
this._selectedTemplate = undefined;
@@ -392,7 +374,7 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps>
}
@action updateRenderedPreviewCollection = async (template: Template) => {
- this._fullyRenderedDocs = this._dataViz ? ((await this.createDocsFromTemplate(this._dataViz, template)).filter(doc => doc).map(doc => doc!) ?? []) : [];
+ this._fullyRenderedDocs = this._dataViz ? ((await this.templateManager.createDocsFromTemplate(this._dataViz, template, this.fieldsInfos, this.DEBUG_MODE)).filter(doc => doc).map(doc => doc!) ?? []) as unknown as Doc[] : [];
this.updateRenderedDocCollection();
};
@@ -517,19 +499,88 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps>
this.forceUpdate();
};
- generatePresetTemplates = async (debug: boolean) => {
+ compileFieldDescriptions = (templates: Template[]): string => {
+ let descriptions: string = '';
+ templates.forEach(template => {
+ descriptions += `---------- NEW TEMPLATE TO INCLUDE: The title is: ${template.title}. Its fields are: `;
+ descriptions += template.descriptionSummary;
+ });
+
+ return descriptions;
+ };
+
+ compileColDescriptions = (cols: Col[]): string => {
+ let descriptions: string = ' ------------- COL DESCRIPTIONS START HERE:';
+ cols.forEach(col => (descriptions += `{title: ${col.title}, sizes: ${String(col.sizes)}, type: ${col.type}, descreiption: ${col.desc}} `));
+
+ return descriptions;
+ };
+
+ getColByTitle = (title: string) => {
+ return this.fieldsInfos.filter(col => col.title === title)[0];
+ };
+
+ @action
+ assignColsToFields = async (templates: Template[], cols: Col[]): Promise<[Template, { [field: number]: Col }][]> => {
+ const fieldDescriptions: string = this.compileFieldDescriptions(templates);
+ const colDescriptions: string = this.compileColDescriptions(cols);
+
+ const inputText = fieldDescriptions.concat(colDescriptions);
+
+ const prompt: string = `(${Math.random() * 100000}) ${inputText}`;
+
+ this._GPTLoading = true;
+
+ try {
+ const res = await gptAPICall(prompt, GPTCallType.TEMPLATE);
+
+ if (res) {
+ const assignments: { [templateTitle: string]: { [fieldID: string]: string } } = JSON.parse(res);
+ const brokenDownAssignments: [Template, { [fieldID: number]: Col }][] = [];
+
+ Object.entries(assignments).forEach(([tempTitle, assignment]) => {
+ const template = templates.filter(temp => temp.title === tempTitle)[0];
+ if (!template) return;
+ const toObj = Object.entries(assignment).reduce(
+ (a, [fieldID, colTitle]) => {
+ const col = this.getColByTitle(colTitle);
+ if (!this._userCreatedFields.includes(col)) {
+ // do the following for any fields not added by the user; will change in the future, for now only GPT content works with user-added field
+ const field = template.getFieldByID(Number(fieldID));
+ field.setContent(col.defaultContent ?? '', col.type === TemplateFieldType.VISUAL ? ViewType.IMG : ViewType.TEXT);
+ field.setTitle(col.title);
+ } else {
+ a[Number(fieldID)] = this.getColByTitle(colTitle);
+ }
+ return a;
+ },
+ {} as { [field: number]: Col }
+ );
+ brokenDownAssignments.push([template, toObj]);
+ });
+
+ return brokenDownAssignments;
+ }
+ } catch (err) {
+ console.error(err);
+ }
+
+ return [];
+ };
+
+ generatePresetTemplates = async () => {
const templates: Template[] = [];
- if (debug) {
+ if (this.DEBUG_MODE) {
templates.push(...this.templateManager.templates);
} else {
this._dataViz?.updateColDefaults();
templates.push(...this.templateManager.getValidTemplates(this.fieldsInfos));
- const assignments = await this.GPTManager.assignColsToFields(templates, this.fieldsInfos);
+ const assignments = await this.assignColsToFields(templates, this.fieldsInfos);
- const renderedTemplatePromises = assignments.map(([template, assgns]) => this.GPTManager.applyGPTContentToTemplate(template, assgns));
+ const renderedTemplatePromises = assignments.map(([template, assgns]) => TemplateMenuAIUtils.applyGPTContentToTemplate(template, assgns));
await Promise.all(renderedTemplatePromises);
}
@@ -542,16 +593,7 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps>
);
};
- @action setVariationTab = (open: boolean) => {
- this._variationsTab = 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);
- }
- }
-
- generateVariations = async (onDoc: Doc): Promise<Doc[]> => {
+ generateVariations = async (onDoc: Doc, prompt: string): Promise<Doc[]> => {
this._loadingVariants = true;
this.variationDocss = [];
const mainCollection = this._dataViz?.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView;
@@ -561,7 +603,7 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps>
clone.x = 10000;
clone.y = 10000;
- await DrawingFillHandler.drawingToImage(clone, 100, this._variationPrompt, undefined, this)
+ await DrawingFillHandler.drawingToImage(clone, 100, prompt, undefined, this)
this._loadingVariants = false;
@@ -591,130 +633,15 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps>
@action setExpandedView = (template: Template | undefined) => {
this._currEditingTemplate = template;
- this._expandedPreview = template?.doc; //Docs.Create.FreeformDocument([doc], { _height: NumListCast(doc._height)[0], _width: NumListCast(doc._width)[0], title: ''});
- };
-
- @action setVariationPrompt = (prompt: string) => {
- this._variationPrompt = prompt;
- }
-
- get editingWindow() {
- const rendered = !this._expandedPreview ? null : (
- <>
- <div className="docCreatorMenu-expanded-template-preview" ref={this.setContainerRef}>
- { this._previewWindow ? <DocumentView
- Document={this._expandedPreview}
- isContentActive={emptyFunction}
- addDocument={returnFalse}
- moveDocument={returnFalse}
- removeDocument={returnFalse}
- PanelWidth={() => this._previewWindow?.clientWidth ?? 1000 - 10}
- PanelHeight={() => this._previewWindow?.clientHeight ?? 1000 - 60}
- ScreenToLocalTransform={() => new Transform(-this._pageX - 5, -this._pageY - 35, 1)}
- renderDepth={5}
- whenChildContentsActiveChanged={emptyFunction}
- focus={emptyFunction}
- styleProvider={DefaultStyleProvider}
- addDocTab={DocumentViewInternal.addDocTabFunc}
- pinToPres={() => undefined}
- childFilters={returnEmptyFilter}
- childFiltersByRanges={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- fitContentsToBox={returnFalse}
- fitWidth={returnFalse}
- /> : null
- }
- </div>
- { this._variationsTab ?
- <div className="docCreatorMenu-section">
- <div className="docCreatorMenu-section-topbar">
- <div className="docCreatorMenu-section-title" style={{color: 'white'}}>Variations</div>
- <button className="docCreatorMenu-menu-button section-reveal-options" onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => (this._menuContent = 'dashboard')))}>
- <FontAwesomeIcon icon="gear" />
- </button>
- </div>
- <div className="docCreatorMenu-templates-preview-window">
- {this._currentVariations.map(variant =>
- <div className="docCreatorMenu-preview-window">
- {this.docPreview(variant)}
- </div>
- )}
- </div>
- <div className="docCreatorMenu-variation-prompt-input">
- <textarea
- className="docCreatorMenu-variation-prompt-input-textbox"
- onChange={e => this.setVariationPrompt(e.target.value)}
- defaultValue={''}
- placeholder={'Enter a custom prompt here (optional)'}
- />
- <button className="docCreatorMenu-menu-button"
- onPointerDown={e => this.setUpButtonClick(e, async () => {
- this._currentVariations = await this.generateVariations(this._currEditingTemplate?.getRenderedDoc()!);
- })
- }>
- <FontAwesomeIcon icon="arrows-rotate" />
- </button>
- </div>
- </div>
- : null
- }
- </>
- );
- return (
- <div className="docCreatorMenu-expanded-template-preview">
- <div className="top-panel" />
- {rendered}
- <div className="right-buttons-panel">
- <button
- className="docCreatorMenu-menu-button section-reveal-options top-right"
- onPointerDown={e =>
- this.setUpButtonClick(e, () => {
- if (!this._currEditingTemplate) return;
- if (this._currEditingTemplate === this._selectedTemplate) {
- this.updateRenderedPreviewCollection(this._currEditingTemplate);
- }
- this.setExpandedView(undefined);
- })
- }>
- <FontAwesomeIcon icon="minimize" />
- </button>
- <button className="docCreatorMenu-menu-button section-reveal-options top-right" onPointerDown={e => this.setUpButtonClick(e, async () => {
- if (!this._currEditingTemplate) return;
- this.setVariationTab(!this._variationsTab);
- })}>
- <FontAwesomeIcon icon="lightbulb" />
- </button>
- </div>
- </div>
- );
- }
+ if (template) {
+ this._menuContent = 'templateEditing';
+ } else {
+ this._menuContent = 'templates';
+ }
- docPreview = (doc: Doc | undefined) =>
- !doc ? null : (
- <DocumentView
- Document={doc}
- isContentActive={emptyFunction} // !!! should be return false
- addDocument={returnFalse}
- moveDocument={returnFalse}
- removeDocument={returnFalse}
- PanelWidth={() => this._menuDimensions.height * .3}
- PanelHeight={() => this._menuDimensions.height * .3}
- ScreenToLocalTransform={() => new Transform(-this._pageX - 5, -this._pageY - 35, 1)}
- renderDepth={1}
- whenChildContentsActiveChanged={emptyFunction}
- focus={emptyFunction}
- styleProvider={DefaultStyleProvider}
- addDocTab={this._props.addDocTab}
- pinToPres={() => undefined}
- childFilters={returnEmptyFilter}
- childFiltersByRanges={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- fitContentsToBox={returnFalse}
- fitWidth={returnFalse}
- hideDecorations={true}
- />
- );
+ //Docs.Create.FreeformDocument([doc], { _height: NumListCast(doc._height)[0], _width: NumListCast(doc._width)[0], title: ''});
+ };
@action updateXMargin = (input: string) => {
this._layout.xMargin = Number(input);
@@ -1137,7 +1064,19 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps>
get renderSelectedViewType() {
switch (this._menuContent) {
- case 'templates': return <SuggestedTemplatesWindow menu={this} setupButtonClick={this.setUpButtonClick}/>;
+ case 'templates':
+ return <TemplateSidescrollView
+ title={'Suggested Templates'}
+ menu={this}
+ setupButtonClick={this.setUpButtonClick}
+ templates={this._suggestedTemplates}
+ />
+ case 'templateEditing':
+ return <TemplateEditingWindow
+ setupButtonClick={this.setUpButtonClick}
+ template={this._currEditingTemplate as Template}
+ menu={this}
+ />
case 'options': return this.optionsMenuContents;
case 'dashboard': return this.dashboardContents;
} // prettier-ignore
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx
new file mode 100644
index 000000000..fe7822087
--- /dev/null
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx
@@ -0,0 +1,134 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, observable, runInAction } from "mobx";
+import React from "react";
+import { returnFalse, returnEmptyFilter } from "../../../../../../ClientUtils";
+import { emptyFunction } from "../../../../../../Utils";
+import { Doc, returnEmptyDoclist } from "../../../../../../fields/Doc";
+import { DefaultStyleProvider } from "../../../../StyleProvider";
+import { DocumentView, DocumentViewInternal } from "../../../DocumentView";
+import { DocCreatorMenu } from "../DocCreatorMenu";
+import { TemplateSidescrollView } from "./TemplatesSidescrollDisplay";
+import { observer } from "mobx-react";
+import { Transform } from "../../../../../util/Transform";
+import { Template } from "../Template";
+import { TemplateMenuAIUtils } from "../Backend/TemplateMenuAIUtils";
+import { ObservableReactComponent } from "../../../../ObservableReactComponent";
+
+interface TemplateEditingWindowProps {
+ menu: DocCreatorMenu;
+ template: Template;
+ setupButtonClick: (e: React.PointerEvent, func: () => void) => void;
+}
+
+@observer
+export class TemplateEditingWindow extends ObservableReactComponent<TemplateEditingWindowProps> {
+
+ private fireflyPrompt: string = 'Use this template to generate an empty baseball card template.';
+ private previewWindow: HTMLDivElement | null = null;
+
+ @observable _variationsTabOpen: boolean = false;
+ @observable _variations: Doc[] = [];
+
+ 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 fireflyVariationsTab() {
+
+ return (
+ <>
+ <TemplateSidescrollView
+ menu={this._props.menu}
+ title={'Generate Variations'}
+ templates={[]}
+ setupButtonClick={this._props.setupButtonClick}
+ />
+ <div className="docCreatorMenu-section">
+ <div className="docCreatorMenu-variation-prompt-input">
+ <textarea
+ className="docCreatorMenu-variation-prompt-input-textbox"
+ onChange={e => this.fireflyPrompt = e.target.value}
+ defaultValue={''}
+ placeholder={'Enter a custom prompt here (optional)'}
+ />
+ <button className="docCreatorMenu-menu-button"
+ onPointerDown={e => this._props.setupButtonClick(e, async () => {
+ this._variations = await this._props.menu.generateVariations(this._props.template.getRenderedDoc()!, this.fireflyPrompt);
+ })
+ }>
+ <FontAwesomeIcon icon="arrows-rotate" />
+ </button>
+ </div>
+ </div>
+ </>
+ )
+ }
+
+ get renderedDocPreview(){
+ const doc: Doc = this._props.template.getRenderedDoc() as unknown as Doc;
+
+ return (
+ <div className="docCreatorMenu-expanded-template-preview" ref={this.setContainerRef}>
+ <DocumentView
+ Document={doc}
+ isContentActive={emptyFunction}
+ addDocument={returnFalse}
+ moveDocument={returnFalse}
+ removeDocument={returnFalse}
+ PanelWidth={() => this.previewWindow?.clientWidth ?? 1000 - 10}
+ PanelHeight={() => this.previewWindow?.clientHeight ?? 1000 - 60}
+ ScreenToLocalTransform={() => new Transform(-this._props.menu._pageX - 5, -this._props.menu._pageY - 35, 1)}
+ renderDepth={5}
+ whenChildContentsActiveChanged={emptyFunction}
+ focus={emptyFunction}
+ styleProvider={DefaultStyleProvider}
+ addDocTab={DocumentViewInternal.addDocTabFunc}
+ pinToPres={() => undefined}
+ childFilters={returnEmptyFilter}
+ childFiltersByRanges={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ fitContentsToBox={returnFalse}
+ fitWidth={returnFalse}
+ />
+ </div>
+ )
+ }
+
+ render() {
+ return (
+ <div className="docCreatorMenu-expanded-template-preview">
+ <div className="top-panel" />
+ {this.renderedDocPreview}
+ {this.fireflyVariationsTab}
+ <div className="right-buttons-panel">
+ <button
+ className="docCreatorMenu-menu-button section-reveal-options top-right"
+ onPointerDown={e =>
+ this._props.setupButtonClick(e, () => {
+ if (this._props.template === this._props.menu._selectedTemplate) {
+ this._props.menu.updateRenderedPreviewCollection(this._props.template);
+ }
+ this._props.menu.setExpandedView(undefined);
+ })
+ }>
+ <FontAwesomeIcon icon="minimize" />
+ </button>
+ <button className="docCreatorMenu-menu-button section-reveal-options top-right" onPointerDown={e => this._props.setupButtonClick(e, async () => {
+ this.setVariationTab(!this._variationsTabOpen);
+ })}>
+ <FontAwesomeIcon icon="lightbulb" />
+ </button>
+ </div>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewBox.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewBox.tsx
index e40192fa8..4759a4158 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewBox.tsx
@@ -6,6 +6,12 @@ import React from "react";
import { ObservableReactComponent } from "../../../../ObservableReactComponent";
import { DocCreatorMenu } from "../DocCreatorMenu";
import { IconProp } from "@fortawesome/fontawesome-svg-core";
+import { DocumentView } from "../../../DocumentView";
+import { emptyFunction } from "../../../../../../Utils";
+import { returnEmptyFilter, returnFalse } from "../../../../../../ClientUtils";
+import { Transform } from "../../../../../util/Transform";
+import { DefaultStyleProvider } from "../../../../StyleProvider";
+import { returnEmptyDoclist } from "../../../../../../fields/Doc";
export interface TemplatePreviewBoxProps {
template: Template;
@@ -27,7 +33,7 @@ export class TemplatePreviewBox extends ObservableReactComponent<TemplatePreview
border: this.props.menu._selectedTemplate === template ? `solid 3px ${Colors.MEDIUM_BLUE}` : '',
boxShadow: this.props.menu._selectedTemplate === template ? `0 0 15px rgba(68, 118, 247, .8)` : '',
}}
- onPointerDown={e => this.props.menu.setUpButtonClick(e, () => runInAction(() => this.props.menu._selectedTemplate = template))}>
+ onPointerDown={e => this.props.menu.setUpButtonClick(e, () => runInAction(() => {console.log('setting'); this.props.menu._selectedTemplate = template}))}>
{ this.props.leftButtonOpts ?
<button
className="option-button left"
@@ -41,7 +47,28 @@ export class TemplatePreviewBox extends ObservableReactComponent<TemplatePreview
<button className="option-button right" onPointerDown={e => this.props.menu.setUpButtonClick(e, () => this.props.rightButtonOpts![1](template))}>
<FontAwesomeIcon icon={this.props.rightButtonOpts![0]} color="white" />
</button> : null }
- {this.props.menu.docPreview(template.getRenderedDoc())}
+ <DocumentView
+ Document={this.props.template.getRenderedDoc()!} //!!! bad
+ isContentActive={emptyFunction} // !!! should be return false
+ addDocument={returnFalse}
+ moveDocument={returnFalse}
+ removeDocument={returnFalse}
+ PanelWidth={() => this.props.menu._menuDimensions.height * .3}
+ PanelHeight={() => this.props.menu._menuDimensions.height * .3}
+ ScreenToLocalTransform={() => new Transform(-this.props.menu._pageX - 5, -this.props.menu._pageY - 35, 1)}
+ renderDepth={1}
+ whenChildContentsActiveChanged={emptyFunction}
+ focus={emptyFunction}
+ styleProvider={DefaultStyleProvider}
+ addDocTab={this.props.menu._props.addDocTab}
+ pinToPres={() => undefined}
+ childFilters={returnEmptyFilter}
+ childFiltersByRanges={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ fitContentsToBox={returnFalse}
+ fitWidth={returnFalse}
+ hideDecorations={true}
+ />
</div>
)
}
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/SuggestedTemplatesWindow.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatesSidescrollDisplay.tsx
index 517a998d9..5035227d1 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/SuggestedTemplatesWindow.tsx
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatesSidescrollDisplay.tsx
@@ -13,20 +13,17 @@ import { TemplatePreviewBox } from "./TemplatePreviewBox";
export interface SuggestedTemplatesProps {
menu: DocCreatorMenu;
+ templates: Template[];
+ previewBoxLeftButtonOpts?: [string, () => any];
+ previewBoxRightButtonOpts?: [string, () => any];
setupButtonClick: (e: React.PointerEvent, func: () => void) => void;
}
@observer
-export class SuggestedTemplatesWindow extends ObservableReactComponent<SuggestedTemplatesProps> {
+export class TemplateSidescrollView extends ObservableReactComponent<SuggestedTemplatesProps> {
@observable _GPTLoading: boolean = false;
- @observable _suggestedTemplates: Template[] = [];
- @observable _userTemplates: Template[] = [];
-
- @action addUserTemplate = (template: Template) => { this._userTemplates.push(template) };
- @action removeUserTemplate = (template: Template) => { this._userTemplates.splice(this._userTemplates.indexOf(template), 1) };
-
render() {
return (
<div className='docCreatorMenu-templates-view'>
@@ -39,12 +36,12 @@ export class SuggestedTemplatesWindow extends ObservableReactComponent<Suggested
</button>
</div>
<div className="docCreatorMenu-templates-preview-window" style={{ justifyContent: this.props.menu._menuDimensions.width > 400 ? 'center' : '' }}>
- {this._suggestedTemplates.map(template => (
+ {this.props.templates.map(template => (
<TemplatePreviewBox
template={template}
menu={this.props.menu}
leftButtonOpts={["magnifying-glass", (template: Template) => { this.props.menu.setExpandedView(template); this.forceUpdate(); }]}
- rightButtonOpts={["plus", (template: Template) => this.addUserTemplate(template)]}
+ rightButtonOpts={["plus", (template: Template) => {}]}
/>
))}
</div>
@@ -56,24 +53,6 @@ export class SuggestedTemplatesWindow extends ObservableReactComponent<Suggested
</div>
</div>
</div>
- <hr className="docCreatorMenu-option-divider full no-margin" />
- <div className="docCreatorMenu-section">
- <div className="docCreatorMenu-section-topbar">
- <div className="docCreatorMenu-section-title">Your Templates</div>
- <button className="docCreatorMenu-menu-button section-reveal-options">
- <FontAwesomeIcon icon="gear" />
- </button>
- </div>
- <div className="docCreatorMenu-templates-preview-window" style={{ justifyContent: this.props.menu._menuDimensions.width > 400 ? 'center' : '' }}>
- {this._userTemplates.map(template => (
- <TemplatePreviewBox
- template={template}
- menu={this.props.menu}
-
- />
- ))}
- </div>
- </div>
</div>
</div>
);
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts
index 1a4384bc1..2bf9f8de5 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts
@@ -8,16 +8,15 @@ import { Conditional } from './Backend/TemplateManager';
export class Template {
_mainField: DynamicField;
- private conditionalLogic: Record<string, Conditional[]>;
+ 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
*/
- constructor(definition: FieldSettings | DynamicField, conditionalLogic: Record<string, Conditional[]>) {
+ constructor(definition: FieldSettings | DynamicField) {
makeAutoObservable(this);
this._mainField = definition instanceof DynamicField ? definition : this.setupMainField(definition);
- this.conditionalLogic = conditionalLogic;
}
get childFields(): TemplateField[] {
@@ -56,7 +55,7 @@ export class Template {
//dispose each subfields disposers, etc.
};
- cloneBase = () => new Template(this._mainField?.makeClone(undefined) ?? TemplateLayouts.BasicSettings, this.conditionalLogic);
+ cloneBase = () => new Template(this._mainField?.makeClone(undefined) ?? TemplateLayouts.BasicSettings);
getRenderedDoc = () => this.doc;
@@ -103,6 +102,14 @@ export class Template {
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);
+ }
+
getMatches = (cols: Col[]): number[][] => {
const numFields = this.contentFields.length;
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/TemplateField.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/TemplateField.ts
index 0ad1accc1..b4ba3fe7d 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/TemplateField.ts
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/TemplateField.ts
@@ -18,7 +18,7 @@ export abstract class TemplateField {
* @param sameId -
* @returns TemplateField
*/
- static CreateField: (settings: FieldSettings, index: number, parent: TemplateField | undefined, template: Template, sameId?: boolean) => TemplateField;
+ static CreateField: (settings: FieldSettings, index: number, parent: TemplateField | undefined, sameId?: boolean) => TemplateField;
protected _parent?: TemplateField;
protected _id: number;
@@ -26,15 +26,13 @@ export abstract class TemplateField {
protected _settings: FieldSettings;
protected _renderDoc: Doc | undefined;
protected _dimensions: FieldDimensions | undefined;
- public template: Template;
- constructor(settings: FieldSettings, id: number = 1, template: Template, parent?: TemplateField) {
+ constructor(settings: FieldSettings, id: number = 1, parent?: TemplateField) {
this._id = id;
this._parent = parent;
this._settings = settings;
this._title = settings.title ?? '';
this._dimensions = this.getLocalDimensions(this._settings, this._parent?.getDimensions);
- this.template = template;
this.applyBasicOpts(this._dimensions, settings);
return this;
}
@@ -75,7 +73,7 @@ export abstract class TemplateField {
makeClone(parent?: TemplateField) {
const settings: FieldSettings = structuredClone(this._settings);
- const cloned = TemplateField.CreateField(settings, this._id, parent, this.template, true); // create a value for this.Document/subfields that we want to ignore
+ const cloned = TemplateField.CreateField(settings, this._id, parent, true); // create a value for this.Document/subfields that we want to ignore
this._renderDoc && Doc.MakeClone(this._renderDoc).then(({ clone }) => (cloned._renderDoc = clone));
cloned._title = this._title;
cloned._dimensions = this._dimensions;
@@ -89,7 +87,7 @@ export abstract class TemplateField {
changeFieldType = (newType: ViewType): TemplateField => {
const newSettings = this._settings;
newSettings.viewType = newType;
- const newField = TemplateField.CreateField(newSettings, this._id, this._parent, this.template, true);
+ const newField = TemplateField.CreateField(newSettings, this._id, this._parent, true);
this._parent?.exchangeFields(newField, this);
return newField;
};
@@ -137,13 +135,13 @@ export type FieldSettings = {
tl: [number, number];
br: [number, number];
opts: DocumentOptions;
- viewType: ViewType;
- title?: string;
- subfields?: FieldSettings[];
types?: TemplateFieldType[];
sizes?: TemplateFieldSize[];
- description?: string;
+ title?: string;
+ viewType: ViewType;
template?: Template;
+ subfields?: FieldSettings[];
+ description?: string;
};
export enum ViewType {
@@ -161,6 +159,3 @@ export type FieldDimensions = {
coord: { x: number; y: number };
};
-export type FieldTree = {
- node: { field: TemplateField; subfields: FieldTree[] };
-};