From a2802df5096cfea2d77371ed9025ce26c8eda222 Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Thu, 24 Oct 2024 04:31:42 -0400 Subject: work on field refactor --- .../DataVizBox/DocCreatorMenu/TemplateBackend.tsx | 491 +++++++++++++++++++++ 1 file changed, 491 insertions(+) create mode 100644 src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx') diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx new file mode 100644 index 000000000..a25eab74f --- /dev/null +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx @@ -0,0 +1,491 @@ +import { FieldOpts, FieldSettings } from "./FieldTypes"; + +export enum TemplateFieldType { + TEXT = 'text', + VISUAL = 'visual', + UNSET = 'unset', +} + +export enum TemplateFieldSize { + TINY = 'tiny', + SMALL = 'small', + MEDIUM = 'medium', + LARGE = 'large', + HUGE = 'huge', +} + +export interface TemplateDocInfos { + title: string; + height: number; + width: number; + opts: FieldOpts; + fields: FieldSettings[]; + decorations: FieldSettings[]; +} + +export class TemplateLayouts { + public static get allTemplates(): TemplateDocInfos[] { + return Object.values(TemplateLayouts).filter(value => typeof value === 'object' && value !== null && 'title' in value) as TemplateDocInfos[]; + } + + public static getTemplateByTitle = (title: string): TemplateDocInfos | undefined => { + switch (title) { + case 'fourfield1': + return TemplateLayouts.FourField001; + case 'fourfield2': + return TemplateLayouts.FourField002; + // case 'fourfield3': + // return TemplateLayouts.FourField003; + case 'fourfield4': + return TemplateLayouts.FourField004; + case 'threefield1': + return TemplateLayouts.ThreeField001; + case 'threefield2': + return TemplateLayouts.ThreeField002; + default: + break; + } + + return undefined; + }; + + public static FourField001: TemplateDocInfos = { + title: 'fourfield1', + width: 416, + height: 700, + opts: { + backgroundColor: '#C0B887', + cornerRounding: 20, + borderColor: '#6B461F', + borderWidth: '12', + }, + fields: [ + { + tl: [-0.95, -1], + br: [0.95, -0.85], + types: [TemplateFieldType.TEXT], + sizes: [TemplateFieldSize.TINY], + description: 'A title field for very short text that contextualizes the content.', + opts: { + backgroundColor: 'transparent', + color: '#F1F0E9', + contentXCentering: 'h-center', + fontBold: true, + }, + }, + { + tl: [-0.87, -0.83], + br: [0.87, 0.2], + types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL], + sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE], + description: 'The main focus of the template; could be an image, long text, etc.', + opts: { + cornerRounding: 20, + borderColor: '#8F5B25', + borderWidth: '6', + backgroundColor: '#CECAB9', + }, + }, + { + tl: [-0.8, 0.2], + br: [0.8, 0.3], + types: [TemplateFieldType.TEXT], + sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL], + description: 'A caption for field #2, very short to short text that contextualizes the content of field #2', + opts: { + backgroundColor: 'transparent', + contentXCentering: 'h-center', + color: '#F1F0E9', + }, + }, + { + tl: [-0.87, 0.37], + br: [0.87, 0.96], + types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL], + sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE], + description: 'A medium-sized field for medium/long text.', + opts: { + cornerRounding: 15, + borderColor: '#8F5B25', + borderWidth: '6', + backgroundColor: '#CECAB9', + }, + }, + ], + decorations: [], + }; + + public static FourField002: TemplateDocInfos = { + title: 'fourfield2', + width: 425, + height: 778, + opts: { + backgroundColor: '#242425', + }, + fields: [ + { + tl: [-0.83, -0.95], + br: [0.83, -0.2], + types: [TemplateFieldType.VISUAL, TemplateFieldType.TEXT], + sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE], + description: 'A medium to large-sized field suitable for an image or longer text that should be the main focus.', + opts: { + borderWidth: '8', + borderColor: '#F8E71C', + }, + }, + { + tl: [-0.65, -0.2], + br: [0.65, -0.02], + types: [TemplateFieldType.TEXT], + sizes: [TemplateFieldSize.TINY], + description: 'A tiny field for just a word or two of plain text.', + opts: { + backgroundColor: 'transparent', + color: 'white', + contentXCentering: 'h-center', + fontTransform: 'uppercase', + }, + }, + { + tl: [-0.65, 0], + br: [0.65, 0.18], + types: [TemplateFieldType.TEXT], + sizes: [TemplateFieldSize.TINY], + description: 'A tiny field for just a word or two of plain text.', + opts: { + backgroundColor: 'transparent', + color: 'white', + contentXCentering: 'h-center', + fontTransform: 'uppercase', + }, + }, + { + tl: [-0.83, 0.2], + br: [0.83, 0.95], + types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL], + sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE], + description: 'A medium to large-sized field suitable for an image or longer text that should be the main focus, or share focus with field 1.', + opts: { + borderWidth: '8', + borderColor: '#F8E71C', + color: 'white', + backgroundColor: '#242425', + }, + }, + ], + decorations: [ + { + tl: [-0.8, -0.075], + br: [-0.525, 0.075], + opts: { + backgroundColor: '#F8E71C', + //rotation: 45, + }, + }, + { + tl: [-0.3075, -0.0245], + br: [-0.2175, 0.0245], + opts: { + backgroundColor: '#F8E71C', + //rotation: 45, + }, + }, + { + tl: [-0.045, -0.0245], + br: [0.045, 0.0245], + opts: { + backgroundColor: '#F8E71C', + //rotation: 45, + }, + }, + { + tl: [0.2175, -0.0245], + br: [0.3075, 0.0245], + opts: { + backgroundColor: '#F8E71C', + //rotation: 45, + }, + }, + { + tl: [0.525, -0.075], + br: [0.8, 0.075], + opts: { + backgroundColor: '#F8E71C', + //rotation: 45, + }, + }, + ], + }; + + // public static FourField003: TemplateDocInfos = { + // title: 'fourfield3', + // width: 477, + // height: 662, + // opts: { + // backgroundColor: '#9E9C95' + // }, + // fields: [{ + // tl: [-.875, -.9], + // br: [.875, .7], + // types: [TemplateFieldType.VISUAL], + // sizes: [TemplateFieldSize.LARGE, TemplateFieldSize.HUGE], + // description: '', + // opts: { + // borderWidth: '15', + // borderColor: '#E0E0DA', + // } + // }, { + // tl: [-.95, .8], + // br: [-.1, .95], + // types: [TemplateFieldType.TEXT], + // sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL], + // description: '', + // opts: { + // backgroundColor: 'transparent', + // color: 'white', + // contentXCentering: 'h-right', + // } + // }, { + // tl: [.1, .8], + // br: [.95, .95], + // types: [TemplateFieldType.TEXT], + // sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL], + // description: '', + // opts: { + // backgroundColor: 'transparent', + // color: 'red', + // fontTransform: 'uppercase', + // contentXCentering: 'h-left' + // } + // }, { + // tl: [0, -.9], + // br: [.85, -.66], + // types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL], + // sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE], + // description: '', + // opts: { + // backgroundColor: 'transparent', + // contentXCentering: 'h-right' + // } + // }], + // decorations: [{ + // tl: [-.025, .8], + // br: [.025, .95], + // opts: { + // backgroundColor: '#E0E0DA', + // } + // }] + // }; + + public static FourField004: TemplateDocInfos = { + title: 'fourfield4', + width: 414, + height: 583, + opts: { + backgroundColor: '#6CCAF0', + borderColor: '#1088C3', + borderWidth: '10', + }, + fields: [ + { + tl: [-0.86, -0.92], + br: [-0.075, -0.77], + types: [TemplateFieldType.TEXT], + sizes: [TemplateFieldSize.TINY], + description: 'A tiny field for just a word or two of plain text.', + opts: { + backgroundColor: '#E2B4F5', + borderWidth: '9', + borderColor: '#9222F1', + contentXCentering: 'h-center', + }, + }, + { + tl: [0.075, -0.92], + br: [0.86, -0.77], + types: [TemplateFieldType.TEXT], + sizes: [TemplateFieldSize.TINY], + description: 'A tiny field for just a word or two of plain text.', + opts: { + backgroundColor: '#F5B4DD', + borderWidth: '9', + borderColor: '#E260F3', + contentXCentering: 'h-center', + }, + }, + { + tl: [-0.81, -0.64], + br: [0.81, 0.48], + types: [TemplateFieldType.VISUAL], + sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE], + description: 'A large to huge field for visual content that is the main content of the template.', + opts: { + borderWidth: '16', + borderColor: '#A2BD77', + }, + }, + { + tl: [-0.86, 0.6], + br: [0.86, 0.92], + types: [TemplateFieldType.TEXT], + sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE], + description: 'A medium to large field for text that describes the visual content above', + opts: { + borderWidth: '9', + borderColor: '#F0D601', + backgroundColor: '#F3F57D', + }, + }, + ], + decorations: [ + { + tl: [-0.852, -0.67], + br: [0.852, 0.51], + opts: { + backgroundColor: 'transparent', + borderColor: '#007C0C', + borderWidth: '10', + }, + }, + ], + }; + + public static ThreeField001: TemplateDocInfos = { + title: 'threefield1', + width: 575, + height: 770, + opts: { + backgroundColor: '#DDD3A9', + }, + fields: [ + { + tl: [-0.66, -0.747], + br: [0.66, 0.247], + types: [TemplateFieldType.VISUAL], + sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE], + description: 'A medium to large field for visual content that is the central focus.', + opts: { + borderColor: 'yellow', + borderWidth: '8', + rotation: 45, + }, + }, + { + tl: [-0.7, 0.2], + br: [0.7, 0.46], + types: [TemplateFieldType.TEXT], + sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL], + description: 'A very small text field for one to a few words. A good caption for the image.', + opts: { + backgroundColor: 'transparent', + contentXCentering: 'h-center', + }, + }, + { + tl: [-0.95, 0.5], + br: [0.95, 0.95], + types: [TemplateFieldType.TEXT], + sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE], + description: 'A medium to large text field for a thorough description of the image. ', + opts: { + backgroundColor: 'transparent', + color: 'white', + }, + }, + ], + decorations: [ + { + tl: [0.2, -1.32], + br: [1.8, -0.66], + opts: { + backgroundColor: '#CEB155', + rotation: 45, + }, + }, + { + tl: [-1.8, -1.32], + br: [-0.2, -0.66], + opts: { + backgroundColor: '#CEB155', + rotation: 135, + }, + }, + { + tl: [0.33, 0.75], + br: [1.66, 1.25], + opts: { + backgroundColor: '#CEB155', + rotation: 135, + }, + }, + { + tl: [-1.66, 0.75], + br: [-0.33, 1.25], + opts: { + backgroundColor: '#CEB155', + rotation: 45, + }, + }, + ], + }; + + public static ThreeField002: TemplateDocInfos = { + title: 'threefield2', + width: 477, + height: 662, + opts: { + backgroundColor: '#9E9C95', + }, + fields: [ + { + tl: [-0.875, -0.9], + br: [0.875, 0.7], + types: [TemplateFieldType.VISUAL], + sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE], + description: 'A medium to large visual field for the main content of the template', + opts: { + borderWidth: '15', + borderColor: '#E0E0DA', + }, + }, + { + tl: [0.1, 0.775], + br: [0.95, 0.975], + types: [TemplateFieldType.TEXT], + sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL], + description: 'A very small text field for one to a few words. The content should represent a general categorization of the image.', + opts: { + backgroundColor: 'transparent', + color: '#AF0D0D', + fontTransform: 'uppercase', + fontBold: true, + contentXCentering: 'h-left', + }, + }, + { + tl: [-0.95, 0.775], + br: [-0.1, 0.975], + types: [TemplateFieldType.TEXT], + sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL], + description: 'A very small text field for one to a few words. The content should contextualize field 2.', + opts: { + backgroundColor: 'transparent', + color: 'black', + contentXCentering: 'h-right', + }, + }, + ], + decorations: [ + { + tl: [-0.025, 0.8], + br: [0.025, 0.95], + opts: { + backgroundColor: '#E0E0DA', + }, + }, + ], + }; +} + + -- cgit v1.2.3-70-g09d2 From 857797d1d47f22b641d1bddb4177028c26677f19 Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Sun, 27 Oct 2024 06:19:21 -0400 Subject: breaking up files --- .../DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx | 2 +- .../nodes/DataVizBox/DocCreatorMenu/FieldTypes.tsx | 362 --------------------- .../DocCreatorMenu/FieldTypes/DynamicField.tsx | 92 ++++++ .../DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx | 57 ++++ .../DocCreatorMenu/FieldTypes/FieldUtils.tsx | 144 ++++++++ .../DocCreatorMenu/FieldTypes/StaticField.tsx | 98 ++++++ .../nodes/DataVizBox/DocCreatorMenu/Template.tsx | 37 +-- .../DataVizBox/DocCreatorMenu/TemplateBackend.tsx | 3 +- 8 files changed, 405 insertions(+), 390 deletions(-) delete mode 100644 src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes.tsx create mode 100644 src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx create mode 100644 src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx create mode 100644 src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils.tsx create mode 100644 src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx') diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx index c86264716..02fd7f869 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx @@ -30,7 +30,7 @@ import { DefaultStyleProvider, returnEmptyDocViewList } from '../../../StyleProv import { Transform } from '../../../../util/Transform'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { TemplateDocInfos, TemplateFieldSize, TemplateFieldType, TemplateLayouts } from './TemplateBackend'; -import { FieldOpts, FieldSettings } from './FieldTypes'; +import { FieldOpts, FieldSettings } from './FieldTypes/StaticField'; export enum LayoutType { STACKED = 'stacked', diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes.tsx deleted file mode 100644 index f30183ed3..000000000 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes.tsx +++ /dev/null @@ -1,362 +0,0 @@ -import { Doc } from "../../../../../fields/Doc"; -import { FieldType } from "../../../../../fields/ObjectField"; -import { Docs, DocumentOptions } from "../../../../documents/Documents"; -import { Col } from "./DocCreatorMenu"; -import { Template } from "./Template"; -import { TemplateDocInfos, TemplateFieldSize, TemplateFieldType } from "./TemplateBackend"; - -export enum FieldContentType { - STRING = 'string', - IMAGE = 'image', -} - -export enum ViewType { - CAROUSEL3D = 'carousel3d', - FREEFORM = 'freeform', -} - -export type FieldDimensions = { - width: number; - height: number; - coord: {x: number, y: number}; -} - -export interface FieldOpts { - backgroundColor?: string; - color?: string; - cornerRounding?: number; - borderWidth?: string; - borderColor?: string; - contentXCentering?: 'h-left' | 'h-center' | 'h-right'; - contentYCentering?: 'top' | 'center' | 'bottom'; - opacity?: number; - rotation?: number; - //animation?: boolean; - fontBold?: boolean; - fontTransform?: 'uppercase' | 'lowercase'; - fieldViewType?: 'freeform' | 'stacked'; -} - -export type FieldSettings = { - tl: [number, number]; - br: [number, number]; - opts: FieldOpts; - dynamicType?: ViewType; - subfields?: FieldSettings[]; - types?: TemplateFieldType[]; - sizes?: TemplateFieldSize[]; - description?: string; -}; - -export interface Field { - getContent: () => string; - setContent: (content: string) => void; - getDimensions: FieldDimensions; - getSubfields: Field[]; - getAllSubfields: Field[]; - setupSubfields: () => Field[]; - renderedDoc: (content: string) => Doc; - matches: () => number[][]; -} - -export class StaticField { - private content: string; - private contentType: FieldContentType | undefined; - private subfields: Field[] = []; - - private settings: FieldSettings; - - private parent: Field; - private dimensions: FieldDimensions; - - constructor(settings: FieldSettings, parent: Field, autoSetupFields: boolean = true) { - this.settings = settings; - this.parent = parent; - this.dimensions = FieldUtils.getLocalDimensions({tl: settings.tl, br: settings.br}, this.parent.getDimensions); - this.content = ''; - if (autoSetupFields) { this.subfields = this.setupSubfields(); } - }; - - get getSubfields(): Field[] { return this.subfields ?? []; }; - - get getAllSubfields(): Field[] { - const fields: Field[] = []; - this.subfields?.forEach(field => { - fields.push(field); - fields.concat(field.getAllSubfields) - }); - return fields; - }; - - get getDimensions() { return this.dimensions }; - - setContent = (newContent: string) => { this.content = newContent }; - getContent() { return this.content }; - - setupSubfields = (): Field[] => { - const fields: Field[] = []; - this.settings.subfields?.forEach(fieldSettings => { - let field: Field; - const dynamicType = fieldSettings.dynamicType; - - if (dynamicType) { - field = new DynamicField(fieldSettings, this, dynamicType); - } else { - field = new StaticField(fieldSettings, this); - } - - fields.push(field); - }); - return fields; - }; - - matches = (): number[][] => { - return []; - }; - - renderedDoc = (content: string): Doc => { - const opts = this.settings.opts; - - if (!this.contentType) { this.contentType = FieldContentType.STRING }; - - switch (this.contentType) { - case FieldContentType.STRING: - const text = String(content); - const textDoc = Docs.Create.TextDocument(text, { - text_fontColor: opts.color, - contentBold: opts.fontBold, - textTransform: opts.fontTransform, - color: opts.color, - _text_fontSize: `${FieldUtils.calculateFontSize(this.dimensions.width, this.dimensions.height, text, true)}` - }); - FieldUtils.applyBasicOpts(textDoc, this.dimensions, this.settings); - return textDoc; - case FieldContentType.IMAGE: - const url = String(content); - const imgDoc = Docs.Create.ImageDocument(url, { - _layout_fitWidth: false, - }); - FieldUtils.applyBasicOpts(imgDoc, this.dimensions, this.settings); - return imgDoc; - } - }; - -} - -class DynamicField implements Field { - private type: ViewType; - private subfields: Field[] = []; - - private settings: FieldSettings; - - private parent: Field; - private dimensions: FieldDimensions; - - constructor(settings: FieldSettings, parent: Field, type: ViewType) { - this.type = type; - this.settings = settings; - this.parent = parent; - this.dimensions = FieldUtils.getLocalDimensions({tl: settings.tl, br: settings.br}, this.parent.getDimensions); - this.subfields = this.setupSubfields(); - } - - setContent = (content: string) => { return }; - getContent = () => { return '' }; - - get getSubfields() { return this.subfields }; - get getAllSubfields() { - const fields: Field[] = []; - this.subfields?.forEach(field => { - fields.push(field); - fields.concat(field.getAllSubfields) - }); - return fields; - }; - - get getDimensions() { return this.dimensions }; - - matches = () => { - return []; - } - - setupSubfields = (): Field[] => { - this.settings.subfields?.forEach(fieldSettings => { - let field: Field; - const dynamicType = fieldSettings.dynamicType; - - if (dynamicType) { field = new DynamicField(fieldSettings, this, dynamicType); } - - field = new StaticField(fieldSettings, this); - this.subfields?.push(field); - }); - return []; - } - - getChildDimensions = (coords: { tl: [number, number]; br: [number, number] }): FieldDimensions => { - const l = (coords.tl[0] * this.dimensions.height) / 2; - const t = coords.tl[1] * this.dimensions.width / 2; //prettier-ignore - const r = (coords.br[0] * this.dimensions.height) / 2; - const b = coords.br[1] * this.dimensions.width / 2; //prettier-ignore - const width = r - l; - const height = b - t; - const coord = { x: l, y: t }; - return { width, height, coord }; - }; - - renderedDoc = (): Doc => { - switch (this.type) { - case ViewType.CAROUSEL3D: - const carouselDoc = Docs.Create.Carousel3DDocument([], { - }); - FieldUtils.applyBasicOpts(carouselDoc, this.dimensions, this.settings); - case ViewType.FREEFORM: - const freeformDoc = Docs.Create.FreeformDocument([], { - }); - FieldUtils.applyBasicOpts(freeformDoc, this.dimensions, this.settings); - default: - return new Doc(); - } - } - -} - -export class TemplateMatcher { - - static matchesForTemplate = (field: FieldSettings, 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(field.subfields?.length) - .fill([]) - .map(() => []); - - field.subfields?.forEach((field, i) => { - cols.forEach((col, v) => { - if (colMatchesField(col, field)) { - matches[i].push(v); - } - }); - }); - - return matches; - }; - - static 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; - }; - -} - - -export class FieldUtils { - public static getLocalDimensions = (coords: { tl: [number, number]; br: [number, number] }, parentDimensions: FieldDimensions): FieldDimensions => { - const l = (coords.tl[0] * parentDimensions.height) / 2; - const t = coords.tl[1] * parentDimensions.width / 2; //prettier-ignore - const r = (coords.br[0] * parentDimensions.height) / 2; - const b = coords.br[1] * parentDimensions.width / 2; //prettier-ignore - const width = r - l; - const height = b - t; - const coord = { x: l, y: t }; - return { width, height, coord }; - }; - - public static applyBasicOpts = (doc: Doc, parentDimensions: FieldDimensions, settings: FieldSettings) => { - const opts = settings.opts; - doc.isDefaultTemplateDoc = true, - doc.x = parentDimensions.coord.x; - doc.y = parentDimensions.coord.y; - doc._height = parentDimensions.height; - doc._width = parentDimensions.width; - doc.backgroundColor = opts.backgroundColor ?? ''; - doc._layout_borderRounding = `${opts.cornerRounding ?? 0}px`; - doc.borderColor = opts.borderColor; - doc.borderWidth = opts.borderWidth; - doc.opacity = opts.opacity; - doc._rotation = opts.rotation; - doc.hCentering = opts.contentXCentering; - }; - - 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; - - 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; - - currFontSize += 1; - } - - return currFontSize - 1; - }; -} \ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx new file mode 100644 index 000000000..361476446 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx @@ -0,0 +1,92 @@ +import { Doc } from "../../../../../../fields/Doc"; +import { Docs } from "../../../../../documents/Documents"; +import { Col } from "../DocCreatorMenu"; +import { TemplateLayouts } from "../TemplateBackend"; +import { Field, FieldDimensions, FieldSettings, ViewType } from "./Field"; +import { FieldUtils } from "./FieldUtils"; +import { StaticField } from "./StaticField"; + +export class DynamicField implements Field { + private type: ViewType; + private subfields: Field[] = []; + + private id: number; + private settings: FieldSettings; + + private parent: Field; + private dimensions: FieldDimensions; + + constructor(settings: FieldSettings, type: ViewType, id: number, parent?: Field) { + this.type = type; + this.id = id; + this.settings = settings; + if (!parent) { + this.parent = this; + this.dimensions = {width: this.settings.br[0] - this.settings.tl[0], height: this.settings.br[1] - this.settings.tl[1], coord: {x: this.settings.tl[0], y: this.settings.tl[1]}}; + } else { + this.parent = parent; + this.dimensions = FieldUtils.getLocalDimensions({tl: settings.tl, br: settings.br}, this.parent.getDimensions); + } + this.subfields = this.setupSubfields(); + } + + setContent = (content: string) => { return }; + getContent = () => { return '' }; + + get getSubfields() { return this.subfields }; + get getAllSubfields() { + const fields: Field[] = []; + this.subfields?.forEach(field => { + fields.push(field); + fields.concat(field.getAllSubfields) + }); + return fields; + }; + + get getDimensions() { return this.dimensions }; + get getID() { return this.id }; + + matches = () => { + return []; + } + + setupSubfields = (): Field[] => { + this.settings.subfields?.forEach(fieldSettings => { + let field: Field; + const dynamicType = fieldSettings.dynamicType; + + if (dynamicType) { field = new DynamicField(fieldSettings, dynamicType, 0, this); } + + field = new StaticField(fieldSettings, this, 0); + this.subfields?.push(field); + }); + return []; + } + + getChildDimensions = (coords: { tl: [number, number]; br: [number, number] }): FieldDimensions => { + const l = (coords.tl[0] * this.dimensions.height) / 2; + const t = coords.tl[1] * this.dimensions.width / 2; //prettier-ignore + const r = (coords.br[0] * this.dimensions.height) / 2; + const b = coords.br[1] * this.dimensions.width / 2; //prettier-ignore + const width = r - l; + const height = b - t; + const coord = { x: l, y: t }; + return { width, height, coord }; + }; + + renderedDoc = (): Doc => { + switch (this.type) { + case ViewType.CAROUSEL3D: + const carouselDoc = Docs.Create.Carousel3DDocument([], { + }); + FieldUtils.applyBasicOpts(carouselDoc, this.dimensions, this.settings); + case ViewType.FREEFORM: + const freeformDoc = Docs.Create.FreeformDocument([], { + }); + FieldUtils.applyBasicOpts(freeformDoc, this.dimensions, this.settings); + default: + return new Doc(); + } + } + +} diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx new file mode 100644 index 000000000..0c1b6e5fc --- /dev/null +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx @@ -0,0 +1,57 @@ +import { Doc } from "../../../../../../fields/Doc"; +import { TemplateFieldSize, TemplateFieldType } from "../TemplateBackend"; + +export interface Field { + getContent: () => string; + setContent: (content: string) => void; + getDimensions: FieldDimensions; + getSubfields: Field[]; + getAllSubfields: Field[]; + getID: number; + setupSubfields: () => Field[]; + renderedDoc: (content: string) => Doc; + matches: () => number[][]; +} + +export type FieldSettings = { + tl: [number, number]; + br: [number, number]; + opts: FieldOpts; + dynamicType?: ViewType; + subfields?: FieldSettings[]; + types?: TemplateFieldType[]; + sizes?: TemplateFieldSize[]; + description?: string; +}; + +export enum FieldContentType { + STRING = 'string', + IMAGE = 'image', +} + +export enum ViewType { + CAROUSEL3D = 'carousel3d', + FREEFORM = 'freeform', +} + +export type FieldDimensions = { + width: number; + height: number; + coord: {x: number, y: number}; +} + +export interface FieldOpts { + backgroundColor?: string; + color?: string; + cornerRounding?: number; + borderWidth?: string; + borderColor?: string; + contentXCentering?: 'h-left' | 'h-center' | 'h-right'; + contentYCentering?: 'top' | 'center' | 'bottom'; + opacity?: number; + rotation?: number; + //animation?: boolean; + fontBold?: boolean; + fontTransform?: 'uppercase' | 'lowercase'; + fieldViewType?: 'freeform' | 'stacked'; +} \ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils.tsx new file mode 100644 index 000000000..ead46d721 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils.tsx @@ -0,0 +1,144 @@ +import { Doc } from "../../../../../../fields/Doc"; +import { Col } from "../DocCreatorMenu"; +import { TemplateFieldSize, TemplateFieldType, TemplateLayouts } from "../TemplateBackend"; +import { FieldDimensions, FieldSettings } from "./Field"; + +export class FieldUtils { + public static getLocalDimensions = (coords: { tl: [number, number]; br: [number, number] }, parentDimensions: FieldDimensions): FieldDimensions => { + const l = (coords.tl[0] * parentDimensions.height) / 2; + const t = coords.tl[1] * parentDimensions.width / 2; //prettier-ignore + const r = (coords.br[0] * parentDimensions.height) / 2; + const b = coords.br[1] * parentDimensions.width / 2; //prettier-ignore + const width = r - l; + const height = b - t; + const coord = { x: l, y: t }; + return { width, height, coord }; + }; + + public static applyBasicOpts = (doc: Doc, parentDimensions: FieldDimensions, settings: FieldSettings) => { + const opts = settings.opts; + doc.isDefaultTemplateDoc = true, + doc.x = parentDimensions.coord.x; + doc.y = parentDimensions.coord.y; + doc._height = parentDimensions.height; + doc._width = parentDimensions.width; + doc.backgroundColor = opts.backgroundColor ?? ''; + doc._layout_borderRounding = `${opts.cornerRounding ?? 0}px`; + doc.borderColor = opts.borderColor; + doc.borderWidth = opts.borderWidth; + doc.opacity = opts.opacity; + doc._rotation = opts.rotation; + doc.hCentering = opts.contentXCentering; + }; + + 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; + + 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; + + currFontSize += 1; + } + + return currFontSize - 1; + }; +} + +export class TemplateMatcher { + + static matchesForTemplate = (field: FieldSettings, 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(field.subfields?.length) + .fill([]) + .map(() => []); + + field.subfields?.forEach((field, i) => { + cols.forEach((col, v) => { + if (colMatchesField(col, field)) { + matches[i].push(v); + } + }); + }); + + return matches; + }; + + static 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: FieldSettings[]) => { + let validTemplates: any[] = []; + templates.forEach(template => { + const numFields = template.subfields?.length; + if (!(numFields === cols.length)) return; + const matches = TemplateMatcher.matchesForTemplate(template, cols); + if (TemplateMatcher.maxMatches(numFields, matches) === numFields) { + validTemplates.push(''); + } + }); + + validTemplates = validTemplates.map(title => TemplateLayouts.getTemplateByTitle(title)); + + return validTemplates; + }; + +} \ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx new file mode 100644 index 000000000..efceae65a --- /dev/null +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx @@ -0,0 +1,98 @@ +import { TbHospital } from "react-icons/tb"; +import { Doc } from "../../../../../../fields/Doc"; +import { FieldType } from "../../../../../../fields/ObjectField"; +import { Docs, DocumentOptions } from "../../../../../documents/Documents"; +import { Col } from "../DocCreatorMenu"; +import { Template } from "../Template"; +import { TemplateDocInfos, TemplateFieldSize, TemplateFieldType, TemplateLayouts } from "../TemplateBackend"; +import { DynamicField } from "./DynamicField"; +import { FieldUtils } from "./FieldUtils"; +import { Field, FieldContentType, FieldDimensions, FieldSettings } from "./Field"; + +export class StaticField { + private content: string; + private contentType: FieldContentType | undefined; + private subfields: Field[] = []; + + private id: number; + + private settings: FieldSettings; + + private parent: Field; + private dimensions: FieldDimensions; + + constructor(settings: FieldSettings, parent: Field, id: number) { + this.settings = settings; + this.id = id; + this.parent = parent; + this.dimensions = FieldUtils.getLocalDimensions({tl: settings.tl, br: settings.br}, this.parent.getDimensions); + this.content = ''; + this.subfields = this.setupSubfields(); + }; + + get getSubfields(): Field[] { return this.subfields ?? []; }; + + get getAllSubfields(): Field[] { + const fields: Field[] = []; + this.subfields?.forEach(field => { + fields.push(field); + fields.concat(field.getAllSubfields) + }); + return fields; + }; + + get getDimensions() { return this.dimensions }; + get getID() { return this.id }; + + setContent = (newContent: string) => { this.content = newContent }; + getContent() { return this.content }; + + setupSubfields = (): Field[] => { + const fields: Field[] = []; + this.settings.subfields?.forEach(fieldSettings => { + let field: Field; + const dynamicType = fieldSettings.dynamicType; + + if (dynamicType) { + field = new DynamicField(fieldSettings, dynamicType, 0, this); + } else { + field = new StaticField(fieldSettings, this, 0); + } + + fields.push(field); + }); + return fields; + }; + + matches = (): number[][] => { + return []; + }; + + renderedDoc = (content: string): Doc => { + const opts = this.settings.opts; + + if (!this.contentType) { this.contentType = FieldContentType.STRING }; + + switch (this.contentType) { + case FieldContentType.STRING: + const text = String(content); + const textDoc = Docs.Create.TextDocument(text, { + text_fontColor: opts.color, + contentBold: opts.fontBold, + textTransform: opts.fontTransform, + color: opts.color, + _text_fontSize: `${FieldUtils.calculateFontSize(this.dimensions.width, this.dimensions.height, text, true)}` + }); + FieldUtils.applyBasicOpts(textDoc, this.dimensions, this.settings); + return textDoc; + case FieldContentType.IMAGE: + const url = String(content); + const imgDoc = Docs.Create.ImageDocument(url, { + _layout_fitWidth: false, + }); + FieldUtils.applyBasicOpts(imgDoc, this.dimensions, this.settings); + return imgDoc; + } + }; + +} \ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx index 57565906b..f05f61b61 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx @@ -1,40 +1,25 @@ import { Doc } from "../../../../../fields/Doc"; import { Docs } from "../../../../documents/Documents"; -import { Field } from "./FieldTypes"; +import { DynamicField } from "./FieldTypes/DynamicField"; +import { Field, FieldSettings, ViewType } from "./FieldTypes/Field"; +import { } from "./FieldTypes/FieldUtils"; +import { } from "./FieldTypes/StaticField"; import { TemplateDocInfos } from "./TemplateBackend"; export class Template { - mainField: Field; - width: number = 0; - height: number = 0; - fields: Field[] = []; + mainField: DynamicField; - constructor(templateInfo: TemplateDocInfos) { - this.width = templateInfo.width; - this.height = templateInfo.height; - this.fields = templateInfo.fields.map(settings => new Field(settings, this)); + constructor(templateInfo: FieldSettings) { + this.mainField = this.setupMainField(templateInfo); } - get childFields(): Field[] { - return this.fields; - } + get childFields(): Field[] { return this.mainField.getSubfields }; + get allFields(): Field[] { return this.mainField.getAllSubfields }; - get allFields(): Field[] { - const fields = this.fields; - this.fields.forEach(field => fields.concat(field.subfields)); - return fields; + setupMainField = (templateInfo: FieldSettings) => { + return new DynamicField(templateInfo, ViewType.FREEFORM, 0); } - getChildDimensions = (coords: { tl: [number, number]; br: [number, number] }): { width: number; height: number; coord: { x: number; y: number } } => { - const l = (coords.tl[0] * this.height) / 2; - const t = coords.tl[1] * this.width / 2; //prettier-ignore - const r = (coords.br[0] * this.height) / 2; - const b = coords.br[1] * this.width / 2; //prettier-ignore - const width = r - l; - const height = b - t; - const coord = { x: l, y: t }; - return { width, height, coord }; - }; } \ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx index a25eab74f..43b0b1572 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx @@ -1,4 +1,5 @@ -import { FieldOpts, FieldSettings } from "./FieldTypes"; +import { FieldOpts, FieldSettings } from "./FieldTypes/Field"; +import { } from "./FieldTypes/StaticField"; export enum TemplateFieldType { TEXT = 'text', -- cgit v1.2.3-70-g09d2 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 ++------------------- .../DocCreatorMenu/FieldTypes/DynamicField.tsx | 15 +- .../DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx | 12 +- .../DocCreatorMenu/FieldTypes/FieldUtils.tsx | 70 ----- .../DocCreatorMenu/FieldTypes/StaticField.tsx | 37 ++- .../nodes/DataVizBox/DocCreatorMenu/Template.tsx | 54 +++- .../DataVizBox/DocCreatorMenu/TemplateBackend.tsx | 134 +++++----- .../DataVizBox/DocCreatorMenu/TemplateManager.tsx | 23 ++ 8 files changed, 215 insertions(+), 420 deletions(-) create mode 100644 src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.tsx (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.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 { 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 { 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 { } }; - 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 => { + fillPresetTemplate = async (template: Template, assignments: { [field: string]: Col }): Promise => { const wordLimit = (size: TemplateFieldSize) => { switch (size) { case TemplateFieldSize.TINY: @@ -665,22 +604,11 @@ export class DocCreatorMenu extends ObservableReactComponent { 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 { 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 => { @@ -715,7 +633,7 @@ export class DocCreatorMenu extends ObservableReactComponent { 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 { 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 { 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 { 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 { 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 { 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 { 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; - }; -} diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx index 361476446..a55f44589 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx @@ -2,7 +2,7 @@ import { Doc } from "../../../../../../fields/Doc"; import { Docs } from "../../../../../documents/Documents"; import { Col } from "../DocCreatorMenu"; import { TemplateLayouts } from "../TemplateBackend"; -import { Field, FieldDimensions, FieldSettings, ViewType } from "./Field"; +import { Field, FieldContentType, FieldDimensions, FieldSettings, ViewType } from "./Field"; import { FieldUtils } from "./FieldUtils"; import { StaticField } from "./StaticField"; @@ -12,6 +12,7 @@ export class DynamicField implements Field { private id: number; private settings: FieldSettings; + private title: string = ''; private parent: Field; private dimensions: FieldDimensions; @@ -30,9 +31,12 @@ export class DynamicField implements Field { this.subfields = this.setupSubfields(); } - setContent = (content: string) => { return }; + setContent = (content: string, type: FieldContentType) => { return }; getContent = () => { return '' }; + setTitle = (title: string) => { this.title = title }; + getTitle = () => { return this.title }; + get getSubfields() { return this.subfields }; get getAllSubfields() { const fields: Field[] = []; @@ -45,15 +49,16 @@ export class DynamicField implements Field { get getDimensions() { return this.dimensions }; get getID() { return this.id }; + get getViewType() { return this.type }; - matches = () => { + matches = (cols: Col[]): Array => { return []; } setupSubfields = (): Field[] => { this.settings.subfields?.forEach(fieldSettings => { let field: Field; - const dynamicType = fieldSettings.dynamicType; + const dynamicType = fieldSettings.viewType; if (dynamicType) { field = new DynamicField(fieldSettings, dynamicType, 0, this); } @@ -78,10 +83,12 @@ export class DynamicField implements Field { switch (this.type) { case ViewType.CAROUSEL3D: const carouselDoc = Docs.Create.Carousel3DDocument([], { + title: this.title, }); FieldUtils.applyBasicOpts(carouselDoc, this.dimensions, this.settings); case ViewType.FREEFORM: const freeformDoc = Docs.Create.FreeformDocument([], { + title: this.title, }); FieldUtils.applyBasicOpts(freeformDoc, this.dimensions, this.settings); default: diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx index 0c1b6e5fc..aa1d1de33 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx @@ -1,23 +1,27 @@ import { Doc } from "../../../../../../fields/Doc"; +import { Col } from "../DocCreatorMenu"; import { TemplateFieldSize, TemplateFieldType } from "../TemplateBackend"; export interface Field { getContent: () => string; - setContent: (content: string) => void; + setContent: (content: string, type: FieldContentType) => void; getDimensions: FieldDimensions; getSubfields: Field[]; getAllSubfields: Field[]; getID: number; + getViewType: ViewType; + getTitle: () => string; + setTitle: (title: string) => void; setupSubfields: () => Field[]; renderedDoc: (content: string) => Doc; - matches: () => number[][]; + matches: (cols: Col[]) => number[]; } export type FieldSettings = { tl: [number, number]; br: [number, number]; opts: FieldOpts; - dynamicType?: ViewType; + viewType: ViewType; subfields?: FieldSettings[]; types?: TemplateFieldType[]; sizes?: TemplateFieldSize[]; @@ -32,6 +36,8 @@ export enum FieldContentType { export enum ViewType { CAROUSEL3D = 'carousel3d', FREEFORM = 'freeform', + STATIC = 'static', + DEC = 'decoration' } export type FieldDimensions = { diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils.tsx index ead46d721..c389704cf 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils.tsx @@ -71,74 +71,4 @@ export class FieldUtils { return currFontSize - 1; }; -} - -export class TemplateMatcher { - - static matchesForTemplate = (field: FieldSettings, 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(field.subfields?.length) - .fill([]) - .map(() => []); - - field.subfields?.forEach((field, i) => { - cols.forEach((col, v) => { - if (colMatchesField(col, field)) { - matches[i].push(v); - } - }); - }); - - return matches; - }; - - static 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: FieldSettings[]) => { - let validTemplates: any[] = []; - templates.forEach(template => { - const numFields = template.subfields?.length; - if (!(numFields === cols.length)) return; - const matches = TemplateMatcher.matchesForTemplate(template, cols); - if (TemplateMatcher.maxMatches(numFields, matches) === numFields) { - validTemplates.push(''); - } - }); - - validTemplates = validTemplates.map(title => TemplateLayouts.getTemplateByTitle(title)); - - return validTemplates; - }; - } \ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx index efceae65a..cd1008429 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx @@ -4,10 +4,9 @@ import { FieldType } from "../../../../../../fields/ObjectField"; import { Docs, DocumentOptions } from "../../../../../documents/Documents"; import { Col } from "../DocCreatorMenu"; import { Template } from "../Template"; -import { TemplateDocInfos, TemplateFieldSize, TemplateFieldType, TemplateLayouts } from "../TemplateBackend"; import { DynamicField } from "./DynamicField"; import { FieldUtils } from "./FieldUtils"; -import { Field, FieldContentType, FieldDimensions, FieldSettings } from "./Field"; +import { Field, FieldContentType, FieldDimensions, FieldSettings, ViewType } from "./Field"; export class StaticField { private content: string; @@ -15,6 +14,7 @@ export class StaticField { private subfields: Field[] = []; private id: number; + private title: string = ''; private settings: FieldSettings; @@ -36,36 +36,55 @@ export class StaticField { const fields: Field[] = []; this.subfields?.forEach(field => { fields.push(field); - fields.concat(field.getAllSubfields) + fields.concat(field.getAllSubfields); }); return fields; }; get getDimensions() { return this.dimensions }; get getID() { return this.id }; + get getViewType() { return ViewType.STATIC }; - setContent = (newContent: string) => { this.content = newContent }; + setContent = (newContent: string, type: FieldContentType) => { + this.content = newContent; + this.contentType = type; + }; getContent() { return this.content }; + setTitle = (title: string) => { this.title = title }; + getTitle = () => { return this.title }; + setupSubfields = (): Field[] => { const fields: Field[] = []; this.settings.subfields?.forEach(fieldSettings => { let field: Field; - const dynamicType = fieldSettings.dynamicType; + const dynamicType = fieldSettings.viewType; if (dynamicType) { field = new DynamicField(fieldSettings, dynamicType, 0, this); } else { field = new StaticField(fieldSettings, this, 0); - } + }; fields.push(field); }); return fields; }; - matches = (): number[][] => { - return []; + matches = (cols: Col[]): number[] => { + const colMatchesField = (col: Col) => { + return this.settings.sizes?.some(size => col.sizes?.includes(size)) && this.settings.types?.includes(col.type); + }; + + const matches: Array = []; + + cols.forEach((col, v) => { + if (colMatchesField(col)) { + matches.push(v); + } + }); + + return matches; }; renderedDoc = (content: string): Doc => { @@ -77,6 +96,7 @@ export class StaticField { case FieldContentType.STRING: const text = String(content); const textDoc = Docs.Create.TextDocument(text, { + title: this.title, text_fontColor: opts.color, contentBold: opts.fontBold, textTransform: opts.fontTransform, @@ -88,6 +108,7 @@ export class StaticField { case FieldContentType.IMAGE: const url = String(content); const imgDoc = Docs.Create.ImageDocument(url, { + title: this.title, _layout_fitWidth: false, }); FieldUtils.applyBasicOpts(imgDoc, this.dimensions, this.settings); diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx index f05f61b61..486fe7f7e 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx @@ -1,11 +1,11 @@ import { Doc } from "../../../../../fields/Doc"; import { Docs } from "../../../../documents/Documents"; +import { Col } from "./DocCreatorMenu"; import { DynamicField } from "./FieldTypes/DynamicField"; import { Field, FieldSettings, ViewType } from "./FieldTypes/Field"; import { } from "./FieldTypes/FieldUtils"; import { } from "./FieldTypes/StaticField"; -import { TemplateDocInfos } from "./TemplateBackend"; export class Template { @@ -17,9 +17,61 @@ export class Template { get childFields(): Field[] { return this.mainField.getSubfields }; get allFields(): Field[] { return this.mainField.getAllSubfields }; + get contentFields(): Field[] { return this.allFields.filter(field => field.getViewType === ViewType.STATIC) }; + + getFieldByID = (id: number): Field => { + return this.allFields.filter(field => field.getID === id)[0]; + } setupMainField = (templateInfo: FieldSettings) => { return new DynamicField(templateInfo, ViewType.FREEFORM, 0); } + isValidTemplate = (cols: Col[]) => { + return this.maxMatches(this.getMatches(cols)) === this.contentFields.length; + } + + getMatches = (cols: Col[]) => { + const matches: number[][] = Array(this.contentFields.length) + .fill([]) + .map(() => []); + + this.contentFields.forEach((field, i) => { + matches[i].concat(field.matches(cols)); + }); + + return matches; + } + + maxMatches = (matches: number[][]) => { + const fieldsCt = this.contentFields.length; + 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; + }; + } \ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx index 43b0b1572..00eb8fab4 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx @@ -1,4 +1,4 @@ -import { FieldOpts, FieldSettings } from "./FieldTypes/Field"; +import { FieldOpts, FieldSettings, ViewType } from "./FieldTypes/Field"; import { } from "./FieldTypes/StaticField"; export enum TemplateFieldType { @@ -15,53 +15,24 @@ export enum TemplateFieldSize { HUGE = 'huge', } -export interface TemplateDocInfos { - title: string; - height: number; - width: number; - opts: FieldOpts; - fields: FieldSettings[]; - decorations: FieldSettings[]; -} - export class TemplateLayouts { - public static get allTemplates(): TemplateDocInfos[] { - return Object.values(TemplateLayouts).filter(value => typeof value === 'object' && value !== null && 'title' in value) as TemplateDocInfos[]; + public static get allTemplates(): FieldSettings[] { + return Object.values(TemplateLayouts).filter(value => typeof value === 'object' && value !== null && 'title' in value) as FieldSettings[]; } - public static getTemplateByTitle = (title: string): TemplateDocInfos | undefined => { - switch (title) { - case 'fourfield1': - return TemplateLayouts.FourField001; - case 'fourfield2': - return TemplateLayouts.FourField002; - // case 'fourfield3': - // return TemplateLayouts.FourField003; - case 'fourfield4': - return TemplateLayouts.FourField004; - case 'threefield1': - return TemplateLayouts.ThreeField001; - case 'threefield2': - return TemplateLayouts.ThreeField002; - default: - break; - } - - return undefined; - }; - - public static FourField001: TemplateDocInfos = { - title: 'fourfield1', - width: 416, - height: 700, + public static FourField001: FieldSettings = { + tl: [0, 0], + br: [416, 700], + viewType: ViewType.FREEFORM, opts: { backgroundColor: '#C0B887', cornerRounding: 20, borderColor: '#6B461F', borderWidth: '12', }, - fields: [ + subfields: [ { + viewType: ViewType.STATIC, tl: [-0.95, -1], br: [0.95, -0.85], types: [TemplateFieldType.TEXT], @@ -75,6 +46,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.87, -0.83], br: [0.87, 0.2], types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL], @@ -88,6 +60,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.8, 0.2], br: [0.8, 0.3], types: [TemplateFieldType.TEXT], @@ -100,6 +73,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.87, 0.37], br: [0.87, 0.96], types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL], @@ -113,18 +87,18 @@ export class TemplateLayouts { }, }, ], - decorations: [], }; - public static FourField002: TemplateDocInfos = { - title: 'fourfield2', - width: 425, - height: 778, + public static FourField002: FieldSettings = { + viewType: ViewType.FREEFORM, + tl: [0,0], + br: [425, 778], opts: { backgroundColor: '#242425', }, - fields: [ + subfields: [ { + viewType: ViewType.STATIC, tl: [-0.83, -0.95], br: [0.83, -0.2], types: [TemplateFieldType.VISUAL, TemplateFieldType.TEXT], @@ -136,6 +110,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.65, -0.2], br: [0.65, -0.02], types: [TemplateFieldType.TEXT], @@ -149,6 +124,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.65, 0], br: [0.65, 0.18], types: [TemplateFieldType.TEXT], @@ -162,6 +138,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.83, 0.2], br: [0.83, 0.95], types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL], @@ -174,46 +151,49 @@ export class TemplateLayouts { backgroundColor: '#242425', }, }, - ], - decorations: [ { + viewType: ViewType.DEC, tl: [-0.8, -0.075], br: [-0.525, 0.075], opts: { backgroundColor: '#F8E71C', - //rotation: 45, + rotation: 45, }, }, { + viewType: ViewType.DEC, tl: [-0.3075, -0.0245], br: [-0.2175, 0.0245], opts: { backgroundColor: '#F8E71C', - //rotation: 45, + rotation: 45, }, }, { + viewType: ViewType.DEC, tl: [-0.045, -0.0245], br: [0.045, 0.0245], opts: { backgroundColor: '#F8E71C', - //rotation: 45, + rotation: 45, }, }, { + viewType: ViewType.DEC, tl: [0.2175, -0.0245], br: [0.3075, 0.0245], opts: { backgroundColor: '#F8E71C', - //rotation: 45, + rotation: 45, }, }, { + viewType: ViewType.DEC, tl: [0.525, -0.075], br: [0.8, 0.075], opts: { backgroundColor: '#F8E71C', - //rotation: 45, + rotation: 45, }, }, ], @@ -279,17 +259,18 @@ export class TemplateLayouts { // }] // }; - public static FourField004: TemplateDocInfos = { - title: 'fourfield4', - width: 414, - height: 583, + public static FourField004: FieldSettings = { + viewType: ViewType.FREEFORM, + tl: [0,0], + br: [414,583], opts: { backgroundColor: '#6CCAF0', borderColor: '#1088C3', borderWidth: '10', }, - fields: [ + subfields: [ { + viewType: ViewType.STATIC, tl: [-0.86, -0.92], br: [-0.075, -0.77], types: [TemplateFieldType.TEXT], @@ -303,6 +284,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [0.075, -0.92], br: [0.86, -0.77], types: [TemplateFieldType.TEXT], @@ -316,6 +298,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.81, -0.64], br: [0.81, 0.48], types: [TemplateFieldType.VISUAL], @@ -327,6 +310,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.86, 0.6], br: [0.86, 0.92], types: [TemplateFieldType.TEXT], @@ -338,9 +322,8 @@ export class TemplateLayouts { backgroundColor: '#F3F57D', }, }, - ], - decorations: [ { + viewType: ViewType.DEC, tl: [-0.852, -0.67], br: [0.852, 0.51], opts: { @@ -352,15 +335,16 @@ export class TemplateLayouts { ], }; - public static ThreeField001: TemplateDocInfos = { - title: 'threefield1', - width: 575, - height: 770, + public static ThreeField001: FieldSettings = { + viewType: ViewType.FREEFORM, + tl: [0,0], + br: [575, 770], opts: { backgroundColor: '#DDD3A9', }, - fields: [ + subfields: [ { + viewType: ViewType.STATIC, tl: [-0.66, -0.747], br: [0.66, 0.247], types: [TemplateFieldType.VISUAL], @@ -373,6 +357,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.7, 0.2], br: [0.7, 0.46], types: [TemplateFieldType.TEXT], @@ -384,6 +369,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.95, 0.5], br: [0.95, 0.95], types: [TemplateFieldType.TEXT], @@ -394,9 +380,8 @@ export class TemplateLayouts { color: 'white', }, }, - ], - decorations: [ { + viewType: ViewType.DEC, tl: [0.2, -1.32], br: [1.8, -0.66], opts: { @@ -405,6 +390,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.DEC, tl: [-1.8, -1.32], br: [-0.2, -0.66], opts: { @@ -413,6 +399,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.DEC, tl: [0.33, 0.75], br: [1.66, 1.25], opts: { @@ -421,6 +408,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.DEC, tl: [-1.66, 0.75], br: [-0.33, 1.25], opts: { @@ -431,15 +419,16 @@ export class TemplateLayouts { ], }; - public static ThreeField002: TemplateDocInfos = { - title: 'threefield2', - width: 477, - height: 662, + public static ThreeField002: FieldSettings = { + viewType: ViewType.FREEFORM, + tl: [0,0], + br: [477, 662], opts: { backgroundColor: '#9E9C95', }, - fields: [ + subfields: [ { + viewType: ViewType.STATIC, tl: [-0.875, -0.9], br: [0.875, 0.7], types: [TemplateFieldType.VISUAL], @@ -451,6 +440,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [0.1, 0.775], br: [0.95, 0.975], types: [TemplateFieldType.TEXT], @@ -465,6 +455,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.95, 0.775], br: [-0.1, 0.975], types: [TemplateFieldType.TEXT], @@ -476,9 +467,8 @@ export class TemplateLayouts { contentXCentering: 'h-right', }, }, - ], - decorations: [ { + viewType: ViewType.DEC, tl: [-0.025, 0.8], br: [0.025, 0.95], opts: { diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.tsx new file mode 100644 index 000000000..890bc6f73 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.tsx @@ -0,0 +1,23 @@ +import { Col } from "./DocCreatorMenu"; +import { FieldSettings } from "./FieldTypes/Field"; +import { Template } from "./Template"; +import { TemplateLayouts } from "./TemplateBackend"; + +export class TemplateManager { + + templates: Template[] = []; + + constructor(templateSettings: FieldSettings[]) { + this.templates = this.initializeTemplates(templateSettings); + } + + initializeTemplates = (templateSettings: FieldSettings[]): Template[] => { + const initializedTemplates: Template[] = []; + templateSettings.forEach(settings => initializedTemplates.push(new Template(settings))); + return initializedTemplates; + } + + getValidTemplates = (cols: Col[]): Template[] => { + return this.templates.filter(template => template.isValidTemplate(cols)); + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 66fcb190ad52aa7e5fc7e0055fed392a569c1972 Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Tue, 29 Oct 2024 18:48:25 -0400 Subject: matching algorithm working --- .../DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx | 4 +++- .../DocCreatorMenu/FieldTypes/DynamicField.tsx | 15 +++++++-------- .../DocCreatorMenu/FieldTypes/StaticField.tsx | 16 ++++++++++------ .../nodes/DataVizBox/DocCreatorMenu/Template.tsx | 21 ++++++++++++++++----- .../DataVizBox/DocCreatorMenu/TemplateBackend.tsx | 2 +- .../DataVizBox/DocCreatorMenu/TemplateManager.tsx | 1 + 6 files changed, 38 insertions(+), 21 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx') diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx index 46266883f..a1de81fa3 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx @@ -469,7 +469,9 @@ export class DocCreatorMenu extends ObservableReactComponent { }; testTemplate = async () => { - this.updateIcons(this._suggestedTemplates.slice()); + + console.log(this.templateManager.templates); + this.forceUpdate(); // try { diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx index 220d938ff..f235d3218 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx @@ -7,7 +7,6 @@ import { FieldUtils } from "./FieldUtils"; import { StaticField } from "./StaticField"; export class DynamicField implements Field { - private type: ViewType; private subfields: Field[] = []; private id: number; @@ -17,8 +16,7 @@ export class DynamicField implements Field { private parent: Field; private dimensions: FieldDimensions; - constructor(settings: FieldSettings, type: ViewType, id: number, parent?: Field) { - this.type = type; + constructor(settings: FieldSettings, id: number, parent?: Field) { this.id = id; this.settings = settings; if (settings.title) { this.title = settings.title }; @@ -50,7 +48,7 @@ export class DynamicField implements Field { get getDimensions() { return this.dimensions }; get getID() { return this.id }; - get getViewType() { return this.type }; + get getViewType() { return this.settings.viewType }; get getDescription(): string { return this.settings.description ?? ''; @@ -61,16 +59,17 @@ export class DynamicField implements Field { } setupSubfields = (): Field[] => { + const fields: Field[] = []; this.settings.subfields?.forEach(fieldSettings => { let field: Field; const dynamicType = fieldSettings.viewType; - if (dynamicType) { field = new DynamicField(fieldSettings, dynamicType, 0, this); } + if (dynamicType) { field = new DynamicField(fieldSettings, 0, this); } field = new StaticField(fieldSettings, this, 0); - this.subfields?.push(field); + fields.push(field); }); - return []; + return fields; } getChildDimensions = (coords: { tl: [number, number]; br: [number, number] }): FieldDimensions => { @@ -85,7 +84,7 @@ export class DynamicField implements Field { }; renderedDoc = (): Doc => { - switch (this.type) { + switch (this.settings.viewType) { case ViewType.CAROUSEL3D: const carouselDoc = Docs.Create.Carousel3DDocument([], { title: this.title, diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx index 809b40520..0f3631858 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx @@ -44,7 +44,7 @@ export class StaticField { get getDimensions() { return this.dimensions }; get getID() { return this.id }; - get getViewType() { return ViewType.STATIC }; + get getViewType() { return this.settings.viewType }; get getDescription(): string { return this.settings.description ?? ''; @@ -63,10 +63,10 @@ export class StaticField { const fields: Field[] = []; this.settings.subfields?.forEach(fieldSettings => { let field: Field; - const dynamicType = fieldSettings.viewType; + const type = fieldSettings.viewType; - if (dynamicType) { - field = new DynamicField(fieldSettings, dynamicType, 0, this); + if (type === ViewType.FREEFORM || type === ViewType.CAROUSEL3D) { + field = new DynamicField(fieldSettings, 0, this); } else { field = new StaticField(fieldSettings, this, 0); }; @@ -78,8 +78,12 @@ export class StaticField { matches = (cols: Col[]): number[] => { const colMatchesField = (col: Col) => { - return this.settings.sizes?.some(size => col.sizes?.includes(size)) && this.settings.types?.includes(col.type); - }; + const isMatch: boolean = ( + this.settings.sizes?.some(size => col.sizes?.includes(size)) + && this.settings.types?.includes(col.type)) + ?? false; + return isMatch; + } const matches: Array = []; diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx index 486fe7f7e..708170359 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx @@ -24,33 +24,44 @@ export class Template { } setupMainField = (templateInfo: FieldSettings) => { - return new DynamicField(templateInfo, ViewType.FREEFORM, 0); + return new DynamicField(templateInfo, 0); } isValidTemplate = (cols: Col[]) => { - return this.maxMatches(this.getMatches(cols)) === this.contentFields.length; + const matches: number[][] = this.getMatches(cols); + const maxMatches: number = this.maxMatches(matches); + return maxMatches === this.contentFields.length; } - getMatches = (cols: Col[]) => { - const matches: number[][] = Array(this.contentFields.length) + getMatches = (cols: Col[]): number[][] => { + const numFields = this.contentFields.length; + + if (cols.length !== numFields) return []; + + const matches: number[][] = Array(numFields) .fill([]) .map(() => []); this.contentFields.forEach((field, i) => { - matches[i].concat(field.matches(cols)); + matches[i] = (field.matches(cols)); }); return matches; } maxMatches = (matches: number[][]) => { + if (matches.length === 0) return 0; + const fieldsCt = this.contentFields.length; const used: boolean[] = Array(fieldsCt).fill(false); const mt: number[] = Array(fieldsCt).fill(-1); + console.log(matches, fieldsCt); + 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; diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx index 00eb8fab4..a23c3ba3d 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx @@ -17,7 +17,7 @@ export enum TemplateFieldSize { export class TemplateLayouts { public static get allTemplates(): FieldSettings[] { - return Object.values(TemplateLayouts).filter(value => typeof value === 'object' && value !== null && 'title' in value) as FieldSettings[]; + return Object.values(TemplateLayouts); } public static FourField001: FieldSettings = { diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.tsx index d47ae802c..535bd423f 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.tsx @@ -30,6 +30,7 @@ export class TemplateManager { } getValidTemplates = (cols: Col[]): Template[] => { + console.log(this.templates.filter(template => template.isValidTemplate(cols))); return this.templates.filter(template => template.isValidTemplate(cols)); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From de18eecdbd0ac99cdc78aef2dd477e341c28df76 Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Tue, 29 Oct 2024 21:45:37 -0400 Subject: content is being filled into fields properly --- src/client/apis/gpt/GPT.ts | 2 +- .../DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx | 108 +++++++++------------ .../DocCreatorMenu/FieldTypes/DynamicField.tsx | 8 +- .../DocCreatorMenu/FieldTypes/StaticField.tsx | 9 +- .../nodes/DataVizBox/DocCreatorMenu/Template.tsx | 4 +- .../DataVizBox/DocCreatorMenu/TemplateBackend.tsx | 5 + .../DataVizBox/DocCreatorMenu/TemplateManager.tsx | 1 - 7 files changed, 62 insertions(+), 75 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx') diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index d38e512bd..19f124bf7 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -66,7 +66,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { prompt: 'List unique differences between the content of the UserAnswer and Rubric. Before each difference, label it and provide any additional information the UserAnswer missed and explain it in second person without separating it into UserAnswer and Rubric content and additional information. If there are no differences, say correct', }, template: { - model: 'gpt-4o-mini', + model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'You will be given a list of field descriptions for multiple templates in the format {field #0: “description”}{field #1: “description”}{...}, and a list of column descriptions in the format {“title”: “description”}{...}. Your job is to match columns with fields based on their descriptions. Your output should be in the following JSON format: {“Template title”:{“#”: “title”, “#”: “title”, “#”: “title” …}, “Template title”:{“#”: “title”, “#”: “title”, “#”: “title” …}} where “Template title” represents the template, # represents the field # and “title” the title of the column assigned to it. A filled out example might look like {“fivefield2”:{“0”:”Name”, “1”:”Image”, “2”:”Caption”, “3”:”Position”, “4”:”Stats”}, “fivefield3”:{0:”Image”, 1:”Name”, 2:”Caption”, 3:”Stats”, 4:”Position”}. Include one object for each template. IT IS VERY IMPORTANT THAT YOU ONLY INCLUDE TEXT IN THE FORMAT ABOVE, WITH NO ADDITIONS WHATSOEVER. Do not include extraneous ‘#’ characters, ‘column’ for columns, or ‘template’ for templates: ONLY THE TITLES AND NUMBERS. There should never be one column assigned to more than one field (ie. if the “name” column is assigned to field 1, it can’t be assigned to any other fields) . Do this for each template whose fields are described. The descriptions are as follows:', diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx index a1de81fa3..405b32e8a 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx @@ -104,7 +104,6 @@ export class DocCreatorMenu extends ObservableReactComponent { makeObservable(this); DocCreatorMenu.Instance = this; this.templateManager = new TemplateManager(TemplateLayouts.allTemplates); - //setTimeout(() => this.generateTemplates('')); } @action setDataViz = (dataViz: DataVizBox) => { @@ -264,7 +263,6 @@ export class DocCreatorMenu extends ObservableReactComponent { } updateIcons = (docs: Doc[]) => { - console.log('called') docs.map(this.getIcon); }; @@ -593,34 +591,6 @@ export class DocCreatorMenu extends ObservableReactComponent { } }; - const renderTextCalls = async (): Promise => { - const rendered: Doc[] = []; - - if (GPTTextCalls.length) { - try { - const prompt = fieldContent + GPTTextAssignment; - - const res = await gptAPICall(prompt, GPTCallType.FILL); - - if (res) { - const assignments: { [title: string]: { number: string; content: string } } = JSON.parse(res); - //console.log('assignments', GPTAssignments, 'assignment string', GPTAssignmentString, 'field content', fieldContent, 'response', res, 'assignments', assignments); - Object.entries(assignments).forEach(([title, info]) => { - const field: Field = template.getFieldByID(Number(info.number)); - const col = this.getColByTitle(title); - - field.setContent(info.content ?? '', FieldContentType.STRING); - field.setTitle(col.title); - }); - } - } catch (err) { - console.log(err); - } - } - - return rendered; - }; - const generateAndLoadImage = async (fieldNum: string, col: Col, prompt: string) => { const url = await this.generateGPTImage(prompt); const field: Field = template.getFieldByID(Number(fieldNum)); @@ -629,33 +599,6 @@ export class DocCreatorMenu extends ObservableReactComponent { field.setTitle(col.title); }; - const renderImageCalls = async (): Promise => { - const rendered: Doc[] = []; - const calls = GPTIMGCalls; - - if (calls.length) { - try { - 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: ' + - fieldContent + - ' **** The user prompt is: ' + - col.desc; - - const prompt = await gptAPICall(sysPrompt, GPTCallType.COMPLETEPROMPT); - - generateAndLoadImage(fieldNum, col, prompt); - }) - ); - } catch (e) { - console.log(e); - } - } - - return rendered; - }; - const GPTTextCalls = Object.entries(assignments).filter(([str, col]) => col.type === TemplateFieldType.TEXT); const GPTIMGCalls = Object.entries(assignments).filter(([str, col]) => col.type === TemplateFieldType.VISUAL); @@ -671,15 +614,50 @@ export class DocCreatorMenu extends ObservableReactComponent { let fieldContent: string = this.templateManager.getContentSummary(template); - const textCalls = await renderTextCalls(); - const imageCalls = await renderImageCalls(); + if (GPTTextCalls.length) { + try { + const prompt = fieldContent + GPTTextAssignment; - return template; - }; + const res = await gptAPICall(prompt, GPTCallType.FILL); - renderTemplates = (templates: Template[]) => { + if (res) { + const assignments: { [title: string]: { number: string; content: string } } = JSON.parse(res); + //console.log('assignments', GPTAssignments, 'assignment string', GPTAssignmentString, 'field content', fieldContent, 'response', res, 'assignments', assignments); + Object.entries(assignments).forEach(([title, info]) => { + const field: Field = template.getFieldByID(Number(info.number)); + const col = this.getColByTitle(title); - } + field.setContent(info.content ?? '', FieldContentType.STRING); + field.setTitle(col.title); + }); + } + } catch (err) { + console.log(err); + } + } + + if (GPTIMGCalls.length) { + try { + await Promise.all( + GPTIMGCalls.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: ' + + fieldContent + + ' **** The user prompt is: ' + + col.desc; + + const prompt = await gptAPICall(sysPrompt, GPTCallType.COMPLETEPROMPT); + + generateAndLoadImage(fieldNum, col, prompt); + }) + ); + } catch (e) { + console.log(e); + } + }; + + return template; + }; compileFieldDescriptions = (templates: Template[]): string => { let descriptions: string = ''; @@ -723,6 +701,7 @@ export class DocCreatorMenu extends ObservableReactComponent { if (res && this._callCount === origCount) { const assignments: { [templateTitle: string]: { [fieldID: string]: string } } = JSON.parse(res); + console.log('assignments', assignments) const brokenDownAssignments: [Template, { [fieldID: number]: Col }][] = []; Object.entries(assignments).forEach(([tempTitle, assignment]) => { @@ -733,6 +712,7 @@ export class DocCreatorMenu extends ObservableReactComponent { const col = this.getColByTitle(colTitle); if (!this._userCreatedColumns.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 fields const field = template.getFieldByID(Number(fieldID)); + //console.log(field); field.setContent(col.defaultContent ?? '', col.type === TemplateFieldType.VISUAL ? FieldContentType.IMAGE : FieldContentType.STRING); field.setTitle(col.title); } else { @@ -766,7 +746,7 @@ export class DocCreatorMenu extends ObservableReactComponent { const renderedTemplates: Template[] = await Promise.all(renderedTemplatePromises); setTimeout(() => { - this.setGSuggestedTemplates(renderedTemplates.map(template => template.mainField.renderedDoc())); + this.setGSuggestedTemplates(templates.map(template => template.mainField.renderedDoc())); this._GPTLoading = false; }); }; diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx index f235d3218..c47db2007 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx @@ -60,13 +60,15 @@ export class DynamicField implements Field { setupSubfields = (): Field[] => { const fields: Field[] = []; - this.settings.subfields?.forEach(fieldSettings => { + this.settings.subfields?.forEach((fieldSettings, index) => { let field: Field; const dynamicType = fieldSettings.viewType; - if (dynamicType) { field = new DynamicField(fieldSettings, 0, this); } + const id = Number(String(this.id) + String(index)); - field = new StaticField(fieldSettings, this, 0); + if (dynamicType) { field = new DynamicField(fieldSettings, id, this); } + + field = new StaticField(fieldSettings, this, id); fields.push(field); }); return fields; diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx index 0f3631858..a0b25485a 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx @@ -53,6 +53,7 @@ export class StaticField { setContent = (newContent: string, type: FieldContentType) => { this.content = newContent; this.contentType = type; + console.log('id: ', this.content) }; getContent() { return this.content }; @@ -61,14 +62,16 @@ export class StaticField { setupSubfields = (): Field[] => { const fields: Field[] = []; - this.settings.subfields?.forEach(fieldSettings => { + this.settings.subfields?.forEach((fieldSettings, index) => { let field: Field; const type = fieldSettings.viewType; + const id = Number(String(this.id) + String(index)); + if (type === ViewType.FREEFORM || type === ViewType.CAROUSEL3D) { - field = new DynamicField(fieldSettings, 0, this); + field = new DynamicField(fieldSettings, id, this); } else { - field = new StaticField(fieldSettings, this, 0); + field = new StaticField(fieldSettings, this, id); }; fields.push(field); diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx index 708170359..69f5cfb81 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx @@ -24,7 +24,7 @@ export class Template { } setupMainField = (templateInfo: FieldSettings) => { - return new DynamicField(templateInfo, 0); + return new DynamicField(templateInfo, 1); } isValidTemplate = (cols: Col[]) => { @@ -56,8 +56,6 @@ export class Template { const used: boolean[] = Array(fieldsCt).fill(false); const mt: number[] = Array(fieldsCt).fill(-1); - console.log(matches, fieldsCt); - const augmentingPath = (v: number): boolean => { if (used[v]) return false; used[v] = true; diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx index a23c3ba3d..166d27228 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx @@ -21,6 +21,7 @@ export class TemplateLayouts { } public static FourField001: FieldSettings = { + title: 'fourfield001', tl: [0, 0], br: [416, 700], viewType: ViewType.FREEFORM, @@ -90,6 +91,7 @@ export class TemplateLayouts { }; public static FourField002: FieldSettings = { + title: 'fourfield002', viewType: ViewType.FREEFORM, tl: [0,0], br: [425, 778], @@ -260,6 +262,7 @@ export class TemplateLayouts { // }; public static FourField004: FieldSettings = { + title: 'fourfield04', viewType: ViewType.FREEFORM, tl: [0,0], br: [414,583], @@ -336,6 +339,7 @@ export class TemplateLayouts { }; public static ThreeField001: FieldSettings = { + title: 'threefield001', viewType: ViewType.FREEFORM, tl: [0,0], br: [575, 770], @@ -420,6 +424,7 @@ export class TemplateLayouts { }; public static ThreeField002: FieldSettings = { + title: 'threefield002', viewType: ViewType.FREEFORM, tl: [0,0], br: [477, 662], diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.tsx index 535bd423f..d47ae802c 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.tsx @@ -30,7 +30,6 @@ export class TemplateManager { } getValidTemplates = (cols: Col[]): Template[] => { - console.log(this.templates.filter(template => template.isValidTemplate(cols))); return this.templates.filter(template => template.isValidTemplate(cols)); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 4b9536c0b4db5922ac33523dc3d4f33d37007982 Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Wed, 30 Oct 2024 03:36:01 -0400 Subject: getAllsubfields works now --- src/client/apis/gpt/GPT.ts | 2 +- .../DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx | 13 ++-- .../DocCreatorMenu/FieldTypes/DynamicField.tsx | 14 +++-- .../DocCreatorMenu/FieldTypes/StaticField.tsx | 4 +- .../nodes/DataVizBox/DocCreatorMenu/Template.tsx | 3 + .../DataVizBox/DocCreatorMenu/TemplateBackend.tsx | 71 ++++++++++++++++++++++ 6 files changed, 94 insertions(+), 13 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx') diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 19f124bf7..80cbc86b3 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -69,7 +69,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, - prompt: 'You will be given a list of field descriptions for multiple templates in the format {field #0: “description”}{field #1: “description”}{...}, and a list of column descriptions in the format {“title”: “description”}{...}. Your job is to match columns with fields based on their descriptions. Your output should be in the following JSON format: {“Template title”:{“#”: “title”, “#”: “title”, “#”: “title” …}, “Template title”:{“#”: “title”, “#”: “title”, “#”: “title” …}} where “Template title” represents the template, # represents the field # and “title” the title of the column assigned to it. A filled out example might look like {“fivefield2”:{“0”:”Name”, “1”:”Image”, “2”:”Caption”, “3”:”Position”, “4”:”Stats”}, “fivefield3”:{0:”Image”, 1:”Name”, 2:”Caption”, 3:”Stats”, 4:”Position”}. Include one object for each template. IT IS VERY IMPORTANT THAT YOU ONLY INCLUDE TEXT IN THE FORMAT ABOVE, WITH NO ADDITIONS WHATSOEVER. Do not include extraneous ‘#’ characters, ‘column’ for columns, or ‘template’ for templates: ONLY THE TITLES AND NUMBERS. There should never be one column assigned to more than one field (ie. if the “name” column is assigned to field 1, it can’t be assigned to any other fields) . Do this for each template whose fields are described. The descriptions are as follows:', + prompt: 'You will be given a list of field descriptions for one or more templates in the format {field #0: “description”}{field #1: “description”}{...}, and a list of column descriptions in the format {“title”: “description”}{...}. Your job is to match columns with fields based on their descriptions. Your output should be in the following JSON format: {“template_title”:{“#”: “title”, “#”: “title”, “#”: “title” …}, “template_title”:{“#”: “title”, “#”: “title”, “#”: “title” …}} where “template_title” is the templates title as specified in the description provided, # represents the field # and “title” the title of the column assigned to it. A filled out example might look like {“fivefield2”:{“0”:”Name”, “1”:”Image”, “2”:”Caption”, “3”:”Position”, “4”:”Stats”}, “fivefield3”:{0:”Image”, 1:”Name”, 2:”Caption”, 3:”Stats”, 4:”Position”}. Include one object for each template. IT IS VERY IMPORTANT THAT YOU ONLY INCLUDE TEXT IN THE FORMAT ABOVE, WITH NO ADDITIONS WHATSOEVER. Do not include extraneous ‘#’ characters, ‘column’ for columns, or ‘template’ for templates: ONLY THE TITLES AND NUMBERS. There should never be one column assigned to more than one field (ie. if the “name” column is assigned to field 1, it can’t be assigned to any other fields) . Do this for each template whose fields are described. The descriptions are as follows:', }, vizsum: { model: 'gpt-4o-mini', diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx index ad7aa7cb9..54f109f84 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx @@ -411,9 +411,11 @@ export class DocCreatorMenu extends ObservableReactComponent { testTemplate = async () => { - console.log(this._suggestedTemplates - ?.map(doc => ({ icon: ImageCast(doc.icon), doc })) - .filter(info => info.icon && info.doc)); + console.log(this.templateManager.templates.map(template => { + template.mainField.getAllSubfields; + }) + + ) this.forceUpdate(); @@ -607,7 +609,8 @@ export class DocCreatorMenu extends ObservableReactComponent { compileFieldDescriptions = (templates: Template[]): string => { let descriptions: string = ''; templates.forEach(template => { - descriptions += `---------- NEW TEMPLATE TO INCLUDE: Description of template ${template.mainField.getTitle()}'s fields: `; + console.log('title: ', template.mainField.getTitle()); + descriptions += `---------- NEW TEMPLATE TO INCLUDE: The title is: ${template.mainField.getTitle()}. Its fields are: `; template.allFields.forEach(field => { descriptions += `{Field #${field.getID}: ${field.getDescription}} `; }); @@ -685,6 +688,8 @@ export class DocCreatorMenu extends ObservableReactComponent { const cols = this.fieldsInfos; const templates = this.templateManager.getValidTemplates(cols); + console.log('templates: ', templates) + const assignments: [Template, { [field: number]: Col }][] = await this.assignColsToFields(templates, cols); const renderedTemplatePromises: Promise