From 691fc130de706c691a42f7e87688c14102016ccc Mon Sep 17 00:00:00 2001
From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com>
Date: Tue, 29 Oct 2024 04:02:29 -0400
Subject: starting to integrate refactor with docreatormenu functionality

---
 .../DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx   | 290 ++-------------------
 1 file changed, 28 insertions(+), 262 deletions(-)

(limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx')

diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx
index 02fd7f869..36264f886 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx
@@ -29,8 +29,10 @@ import './DocCreatorMenu.scss';
 import { DefaultStyleProvider, returnEmptyDocViewList } from '../../../StyleProvider';
 import { Transform } from '../../../../util/Transform';
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { TemplateDocInfos, TemplateFieldSize, TemplateFieldType, TemplateLayouts } from './TemplateBackend';
-import { FieldOpts, FieldSettings } from './FieldTypes/StaticField';
+import { TemplateFieldSize, TemplateFieldType, TemplateLayouts } from './TemplateBackend';
+import { TemplateManager } from './TemplateManager';
+import { Template } from './Template';
+import { Field, FieldContentType } from './FieldTypes/Field';
 
 export enum LayoutType {
     STACKED = 'stacked',
@@ -49,6 +51,8 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
 
     private _ref: HTMLDivElement | null = null;
 
+    private templateManager: TemplateManager; 
+
     @observable _fullyRenderedDocs: Doc[] = [];
     @observable _renderedDocCollectionPreview: Doc | undefined = undefined;
     @observable _renderedDocCollection: Doc | undefined = undefined;
@@ -99,6 +103,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
         super(props);
         makeObservable(this);
         DocCreatorMenu.Instance = this;
+        this.templateManager = new TemplateManager(TemplateLayouts.allTemplates);
         //setTimeout(() => this.generateTemplates(''));
     }
 
@@ -562,79 +567,13 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
         }
     };
 
-    matchesForTemplate = (template: TemplateDocInfos, cols: Col[]): number[][] => {
-        const colMatchesField = (col: Col, field: FieldSettings) => {
-            return field.sizes?.some(size => col.sizes?.includes(size)) && field.types?.includes(col.type);
-        };
-
-        const matches: number[][] = Array(template.fields.length)
-            .fill([])
-            .map(() => []);
-
-        template.fields.forEach((field, i) => {
-            cols.forEach((col, v) => {
-                if (colMatchesField(col, field)) {
-                    matches[i].push(v);
-                }
-            });
-        });
-
-        return matches;
-    };
-
-    maxMatches = (fieldsCt: number, matches: number[][]) => {
-        const used: boolean[] = Array(fieldsCt).fill(false);
-        const mt: number[] = Array(fieldsCt).fill(-1);
-
-        const augmentingPath = (v: number): boolean => {
-            if (used[v]) return false;
-            used[v] = true;
-            for (const to of matches[v]) {
-                if (mt[to] === -1 || augmentingPath(mt[to])) {
-                    mt[to] = v;
-                    return true;
-                }
-            }
-            return false;
-        };
-
-        for (let v = 0; v < fieldsCt; ++v) {
-            used.fill(false);
-            augmentingPath(v);
-        }
-
-        let count: number = 0;
-
-        for (let i = 0; i < fieldsCt; ++i) {
-            if (mt[i] !== -1) ++count;
-        }
-
-        return count;
-    };
-
-    findValidTemplates = (cols: Col[], templates: TemplateDocInfos[]) => {
-        let validTemplates: any[] = [];
-        templates.forEach(template => {
-            const numFields = template.fields.length;
-            if (!(numFields === cols.length)) return;
-            const matches = this.matchesForTemplate(template, cols);
-            if (this.maxMatches(numFields, matches) === numFields) {
-                validTemplates.push(template.title);
-            }
-        });
-
-        validTemplates = validTemplates.map(title => TemplateLayouts.getTemplateByTitle(title));
-
-        return validTemplates;
-    };
-
     /**
      * 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
      */
-    fillPresetTemplate = async (template: TemplateDocInfos, assignments: { [field: string]: Col }): Promise<Doc> => {
+    fillPresetTemplate = async (template: Template, assignments: { [field: string]: Col }): Promise<Doc> => {
         const wordLimit = (size: TemplateFieldSize) => {
             switch (size) {
                 case TemplateFieldSize.TINY:
@@ -665,22 +604,11 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
                         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: FieldSettings = template.fields[Number(info.number)];
+                            const field: Field = template.getFieldByID(Number(info.number));
                             const col = this.getColByTitle(title);
 
-                            const doc = FieldUtils.TextField(
-                                {
-                                    tl: field.tl,
-                                    br: field.br,
-                                },
-                                template.height,
-                                template.width,
-                                col.title,
-                                info.content ?? '',
-                                field.opts
-                            );
-
-                            rendered.push(doc);
+                            field.setContent(info.content ?? '', FieldContentType.STRING);
+                            field.setTitle(col.title);
                         });
                     }
                 } catch (err) {
@@ -691,22 +619,12 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
             return rendered;
         };
 
-        const createGeneratedImage = async (fieldNum: string, col: Col, prompt: string) => {
+        const generateAndLoadImage = async (fieldNum: string, col: Col, prompt: string) => {
             const url = await this.generateGPTImage(prompt);
-            const field: FieldSettings = template.fields[Number(fieldNum)];
-            const doc = FieldUtils.ImageField(
-                {
-                    tl: field.tl,
-                    br: field.br,
-                },
-                template.height,
-                template.width,
-                col.title,
-                url ?? '',
-                field.opts
-            );
+            const field: Field = template.getFieldByID(Number(fieldNum));
 
-            return doc;
+            field.setContent(url ?? '', FieldContentType.IMAGE);
+            field.setTitle(col.title);
         };
 
         const renderImageCalls = async (): Promise<Doc[]> => {
@@ -715,7 +633,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
 
             if (calls.length) {
                 try {
-                    const renderedImages: Doc[] = await Promise.all(
+                    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. 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: ' +
@@ -724,14 +642,10 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
                                 col.desc;
 
                             const prompt = await gptAPICall(sysPrompt, GPTCallType.COMPLETEPROMPT);
-                            //console.log(sysPrompt, prompt);
 
-                            return createGeneratedImage(fieldNum, col, prompt);
+                            generateAndLoadImage(fieldNum, col, prompt);
                         })
                     );
-
-                    const renderedTemplates: Doc[] = await Promise.all(renderedImages);
-                    renderedTemplates.forEach(doc => rendered.push(doc));
                 } catch (e) {
                     console.log(e);
                 }
@@ -740,12 +654,12 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
             return rendered;
         };
 
-        const fields: Doc[] = [];
+        const fields: Field[] = [];
 
-        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 autoLoadedFields = Object.entries(assignments).filter(([f, col]) => this._columns.includes(col));
+        const userCreatedFields: [string, Col][] = Object.entries(assignments).filter(a => !autoLoadedFields.includes(a));
+        const GPTTextCalls = autoLoadedFields.filter(([str, col]) => col.type === TemplateFieldType.TEXT);
+        const GPTIMGCalls = autoLoadedFields.filter(([str, col]) => col.type === TemplateFieldType.VISUAL);
 
         const stringifyGPTInfo = (calls: [string, Col][]): string => {
             let string: string = '*** COLUMN INFO:';
@@ -759,9 +673,9 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
 
         let fieldContent: string = '';
 
-        Object.entries(nonGPTAssignments).forEach(([f, strCol]) => {
-            const field: FieldSettings = template.fields[Number(f)];
-            const col = strCol[1];
+        Object.entries(userCreatedFields).forEach(([fieldID, strAndCol]) => {
+            const field: Field = template.getFieldByID(Number(fieldID));
+            const col = strAndCol[1];
 
             const doc = (col.type === TemplateFieldType.VISUAL ? FieldUtils.ImageField : FieldUtils.TextField)(
                 {
@@ -775,7 +689,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
                 field.opts
             );
 
-            fieldContent += `--- Field #${f} (title: ${col.title}): ${col.defaultContent ?? ''} ---`;
+            fieldContent += `--- Field #${fieldID} (title: ${col.title}): ${col.defaultContent ?? ''} ---`;
 
             fields.push(doc);
         });
@@ -829,7 +743,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
         return createMainDoc();
     };
 
-    compileFieldDescriptions = (templates: TemplateDocInfos[]): string => {
+    compileFieldDescriptions = (templates: Template[]): string => {
         let descriptions: string = '';
         templates.forEach(template => {
             descriptions += `----------  NEW TEMPLATE TO INCLUDE: Description of template ${template.title}'s fields: `;
@@ -898,7 +812,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
         this._dataViz?.updateColDefaults();
 
         const cols = this.fieldsInfos;
-        const templates = this.findValidTemplates(cols, TemplateLayouts.allTemplates);
+        const templates = this.templateManager.getValidTemplates(cols);
 
         const assignments: [TemplateDocInfos, { [field: number]: Col }][] = await this.assignColsToFields(templates, cols);
 
@@ -1668,151 +1582,3 @@ export type Col = {
     type: TemplateFieldType;
     defaultContent?: string;
 };
-
-type DecorationField = FieldSettings;
-
-type InkDecoration = {};
-
-type TemplateDecorations = FieldSettings | InkDecoration;
-
-interface TemplateOpts extends FieldOpts {}
-
-export class FieldUtils {
-    public static calculateFontSize = (contWidth: number, contHeight: number, text: string, uppercase: boolean): number => {
-        const words: string[] = text.split(/\s+/).filter(Boolean);
-
-        let currFontSize = 1;
-        let rowsCount = 1;
-        let currTextHeight = currFontSize * rowsCount * 2;
-
-        while (currTextHeight <= contHeight) {
-            let wordIndex = 0;
-            let currentRowWidth = 0;
-            let wordsInCurrRow = 0;
-            rowsCount = 1;
-
-            while (wordIndex < words.length) {
-                const word = words[wordIndex];
-                const wordWidth = word.length * currFontSize * 0.5;
-                //console.log(wordWidth)
-
-                if (currentRowWidth + wordWidth <= contWidth) {
-                    currentRowWidth += wordWidth;
-                    ++wordsInCurrRow;
-                } else {
-                    if (words.length !== 1 && words.length > wordsInCurrRow) {
-                        rowsCount++;
-                        currentRowWidth = wordWidth;
-                        wordsInCurrRow = 1;
-                    } else {
-                        break;
-                    }
-                }
-
-                wordIndex++;
-            }
-
-            currTextHeight = rowsCount * currFontSize * 2;
-            //console.log(rowsCount, currFontSize, currTextHeight)
-
-            currFontSize += 1;
-        }
-
-        return currFontSize - 1;
-    };
-
-    private static getDimensions = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number): { width: number; height: number; coord: { x: number; y: number } } => {
-        const l = (coords.tl[0] * parentHeight) / 2;
-        const t = coords.tl[1] * parentWidth / 2; //prettier-ignore
-        const r = (coords.br[0] * parentHeight) / 2;
-        const b = coords.br[1] * parentWidth / 2; //prettier-ignore
-        const width = r - l;
-        const height = b - t;
-        const coord = { x: l, y: t };
-        //console.log(coords, parentWidth, parentHeight, height);
-        return { width, height, coord };
-    };
-
-    public static FreeformField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, content: string, opts: FieldOpts) => {
-        const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight);
-
-        const docWithBasicOpts = Docs.Create.FreeformDocument([], {
-            isDefaultTemplateDoc: true,
-            _height: height,
-            _width: width,
-            title: title,
-            x: coord.x,
-            y: coord.y,
-            backgroundColor: opts.backgroundColor ?? '',
-            _layout_borderRounding: `${opts.cornerRounding ?? 0}px`,
-            borderColor: opts.borderColor,
-            borderWidth: opts.borderWidth,
-            opacity: opts.opacity,
-            hCentering: opts.contentXCentering,
-            _rotation: opts.rotation,
-        });
-
-        return docWithBasicOpts;
-    };
-
-    public static TextField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, content: string, opts: FieldOpts) => {
-        const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight);
-
-        const docWithBasicOpts = Docs.Create.TextDocument(content, {
-            isDefaultTemplateDoc: true,
-            _height: height,
-            _width: width,
-            title: title,
-            x: coord.x,
-            y: coord.y,
-            _text_fontSize: `${FieldUtils.calculateFontSize(width, height, content, true)}`,
-            backgroundColor: opts.backgroundColor ?? '',
-            text_fontColor: opts.color,
-            contentBold: opts.fontBold,
-            textTransform: opts.fontTransform,
-            color: opts.color,
-            _layout_borderRounding: `${opts.cornerRounding ?? 0}px`,
-            borderColor: opts.borderColor,
-            borderWidth: opts.borderWidth,
-            opacity: opts.opacity,
-            hCentering: opts.contentXCentering,
-            _rotation: opts.rotation,
-        });
-
-        docWithBasicOpts._layout_hideScroll = true;
-
-        return docWithBasicOpts;
-    };
-
-    public static ImageField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, content: string, opts: FieldOpts) => {
-        const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight);
-
-        const doc = Docs.Create.ImageDocument(content, {
-            isDefaultTemplateDoc: true,
-            _height: height,
-            _width: width,
-            title: title,
-            x: coord.x,
-            y: coord.y,
-            _layout_fitWidth: false,
-            backgroundColor: opts.backgroundColor ?? '',
-            _layout_borderRounding: `${opts.cornerRounding ?? 0}px`,
-            borderColor: opts.borderColor,
-            borderWidth: opts.borderWidth,
-            opacity: opts.opacity,
-            _rotation: opts.rotation,
-        });
-
-        //setTimeout(() => {doc._height = height; doc._width = width}, 10);
-
-        return doc;
-    };
-
-    public static CarouselField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, fields: Doc[]) => {
-        const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight);
-
-        const doc = Docs.Create.Carousel3DDocument(fields, { _height: height, _width: width, title: title, x: coord.x, y: coord.y, _text_fontSize: `${height / 2}` });
-
-        return doc;
-    };
-}
-- 
cgit v1.2.3-70-g09d2

