diff options
Diffstat (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx')
-rw-r--r-- | src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx | 290 |
1 files changed, 28 insertions, 262 deletions
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx index 02fd7f869..36264f886 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx @@ -29,8 +29,10 @@ import './DocCreatorMenu.scss'; import { DefaultStyleProvider, returnEmptyDocViewList } from '../../../StyleProvider'; import { Transform } from '../../../../util/Transform'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; -import { TemplateDocInfos, TemplateFieldSize, TemplateFieldType, TemplateLayouts } from './TemplateBackend'; -import { FieldOpts, FieldSettings } from './FieldTypes/StaticField'; +import { TemplateFieldSize, TemplateFieldType, TemplateLayouts } from './TemplateBackend'; +import { TemplateManager } from './TemplateManager'; +import { Template } from './Template'; +import { Field, FieldContentType } from './FieldTypes/Field'; export enum LayoutType { STACKED = 'stacked', @@ -49,6 +51,8 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { private _ref: HTMLDivElement | null = null; + private templateManager: TemplateManager; + @observable _fullyRenderedDocs: Doc[] = []; @observable _renderedDocCollectionPreview: Doc | undefined = undefined; @observable _renderedDocCollection: Doc | undefined = undefined; @@ -99,6 +103,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { super(props); makeObservable(this); DocCreatorMenu.Instance = this; + this.templateManager = new TemplateManager(TemplateLayouts.allTemplates); //setTimeout(() => this.generateTemplates('')); } @@ -562,79 +567,13 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { } }; - matchesForTemplate = (template: TemplateDocInfos, cols: Col[]): number[][] => { - const colMatchesField = (col: Col, field: FieldSettings) => { - return field.sizes?.some(size => col.sizes?.includes(size)) && field.types?.includes(col.type); - }; - - const matches: number[][] = Array(template.fields.length) - .fill([]) - .map(() => []); - - template.fields.forEach((field, i) => { - cols.forEach((col, v) => { - if (colMatchesField(col, field)) { - matches[i].push(v); - } - }); - }); - - return matches; - }; - - maxMatches = (fieldsCt: number, matches: number[][]) => { - const used: boolean[] = Array(fieldsCt).fill(false); - const mt: number[] = Array(fieldsCt).fill(-1); - - const augmentingPath = (v: number): boolean => { - if (used[v]) return false; - used[v] = true; - for (const to of matches[v]) { - if (mt[to] === -1 || augmentingPath(mt[to])) { - mt[to] = v; - return true; - } - } - return false; - }; - - for (let v = 0; v < fieldsCt; ++v) { - used.fill(false); - augmentingPath(v); - } - - let count: number = 0; - - for (let i = 0; i < fieldsCt; ++i) { - if (mt[i] !== -1) ++count; - } - - return count; - }; - - findValidTemplates = (cols: Col[], templates: TemplateDocInfos[]) => { - let validTemplates: any[] = []; - templates.forEach(template => { - const numFields = template.fields.length; - if (!(numFields === cols.length)) return; - const matches = this.matchesForTemplate(template, cols); - if (this.maxMatches(numFields, matches) === numFields) { - validTemplates.push(template.title); - } - }); - - validTemplates = validTemplates.map(title => TemplateLayouts.getTemplateByTitle(title)); - - return validTemplates; - }; - /** * Populates a preset template framework with content from a datavizbox or any AI-generated content. * @param template the preloaded template framework being filled in * @param assignments a list of template field numbers (from top to bottom) and their assigned columns from the linked dataviz * @returns a doc containing the fully rendered template */ - fillPresetTemplate = async (template: TemplateDocInfos, assignments: { [field: string]: Col }): Promise<Doc> => { + fillPresetTemplate = async (template: Template, assignments: { [field: string]: Col }): Promise<Doc> => { const wordLimit = (size: TemplateFieldSize) => { switch (size) { case TemplateFieldSize.TINY: @@ -665,22 +604,11 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { const assignments: { [title: string]: { number: string; content: string } } = JSON.parse(res); //console.log('assignments', GPTAssignments, 'assignment string', GPTAssignmentString, 'field content', fieldContent, 'response', res, 'assignments', assignments); Object.entries(assignments).forEach(([title, info]) => { - const field: FieldSettings = template.fields[Number(info.number)]; + const field: Field = template.getFieldByID(Number(info.number)); const col = this.getColByTitle(title); - const doc = FieldUtils.TextField( - { - tl: field.tl, - br: field.br, - }, - template.height, - template.width, - col.title, - info.content ?? '', - field.opts - ); - - rendered.push(doc); + field.setContent(info.content ?? '', FieldContentType.STRING); + field.setTitle(col.title); }); } } catch (err) { @@ -691,22 +619,12 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { return rendered; }; - const createGeneratedImage = async (fieldNum: string, col: Col, prompt: string) => { + const generateAndLoadImage = async (fieldNum: string, col: Col, prompt: string) => { const url = await this.generateGPTImage(prompt); - const field: FieldSettings = template.fields[Number(fieldNum)]; - const doc = FieldUtils.ImageField( - { - tl: field.tl, - br: field.br, - }, - template.height, - template.width, - col.title, - url ?? '', - field.opts - ); + const field: Field = template.getFieldByID(Number(fieldNum)); - return doc; + field.setContent(url ?? '', FieldContentType.IMAGE); + field.setTitle(col.title); }; const renderImageCalls = async (): Promise<Doc[]> => { @@ -715,7 +633,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { if (calls.length) { try { - const renderedImages: Doc[] = await Promise.all( + await Promise.all( calls.map(async ([fieldNum, col]) => { const sysPrompt = 'Your job is to create a prompt for an AI image generator to help it generate an image based on existing content in a template and a user prompt. Your prompt should focus heavily on visual elements to help the image generator; avoid unecessary info that might distract it. ONLY INCLUDE THE PROMPT, NO OTHER TEXT OR EXPLANATION. The existing content is as follows: ' + @@ -724,14 +642,10 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { col.desc; const prompt = await gptAPICall(sysPrompt, GPTCallType.COMPLETEPROMPT); - //console.log(sysPrompt, prompt); - return createGeneratedImage(fieldNum, col, prompt); + generateAndLoadImage(fieldNum, col, prompt); }) ); - - const renderedTemplates: Doc[] = await Promise.all(renderedImages); - renderedTemplates.forEach(doc => rendered.push(doc)); } catch (e) { console.log(e); } @@ -740,12 +654,12 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { return rendered; }; - const fields: Doc[] = []; + const fields: Field[] = []; - const GPTAssignments = Object.entries(assignments).filter(([f, col]) => this._columns.includes(col)); - const nonGPTAssignments: [string, Col][] = Object.entries(assignments).filter(a => !GPTAssignments.includes(a)); - const GPTTextCalls = GPTAssignments.filter(([str, col]) => col.type === TemplateFieldType.TEXT); - const GPTIMGCalls = GPTAssignments.filter(([str, col]) => col.type === TemplateFieldType.VISUAL); + const autoLoadedFields = Object.entries(assignments).filter(([f, col]) => this._columns.includes(col)); + const userCreatedFields: [string, Col][] = Object.entries(assignments).filter(a => !autoLoadedFields.includes(a)); + const GPTTextCalls = autoLoadedFields.filter(([str, col]) => col.type === TemplateFieldType.TEXT); + const GPTIMGCalls = autoLoadedFields.filter(([str, col]) => col.type === TemplateFieldType.VISUAL); const stringifyGPTInfo = (calls: [string, Col][]): string => { let string: string = '*** COLUMN INFO:'; @@ -759,9 +673,9 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { let fieldContent: string = ''; - Object.entries(nonGPTAssignments).forEach(([f, strCol]) => { - const field: FieldSettings = template.fields[Number(f)]; - const col = strCol[1]; + Object.entries(userCreatedFields).forEach(([fieldID, strAndCol]) => { + const field: Field = template.getFieldByID(Number(fieldID)); + const col = strAndCol[1]; const doc = (col.type === TemplateFieldType.VISUAL ? FieldUtils.ImageField : FieldUtils.TextField)( { @@ -775,7 +689,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { field.opts ); - fieldContent += `--- Field #${f} (title: ${col.title}): ${col.defaultContent ?? ''} ---`; + fieldContent += `--- Field #${fieldID} (title: ${col.title}): ${col.defaultContent ?? ''} ---`; fields.push(doc); }); @@ -829,7 +743,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { return createMainDoc(); }; - compileFieldDescriptions = (templates: TemplateDocInfos[]): string => { + compileFieldDescriptions = (templates: Template[]): string => { let descriptions: string = ''; templates.forEach(template => { descriptions += `---------- NEW TEMPLATE TO INCLUDE: Description of template ${template.title}'s fields: `; @@ -898,7 +812,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { this._dataViz?.updateColDefaults(); const cols = this.fieldsInfos; - const templates = this.findValidTemplates(cols, TemplateLayouts.allTemplates); + const templates = this.templateManager.getValidTemplates(cols); const assignments: [TemplateDocInfos, { [field: number]: Col }][] = await this.assignColsToFields(templates, cols); @@ -1668,151 +1582,3 @@ export type Col = { type: TemplateFieldType; defaultContent?: string; }; - -type DecorationField = FieldSettings; - -type InkDecoration = {}; - -type TemplateDecorations = FieldSettings | InkDecoration; - -interface TemplateOpts extends FieldOpts {} - -export class FieldUtils { - public static calculateFontSize = (contWidth: number, contHeight: number, text: string, uppercase: boolean): number => { - const words: string[] = text.split(/\s+/).filter(Boolean); - - let currFontSize = 1; - let rowsCount = 1; - let currTextHeight = currFontSize * rowsCount * 2; - - while (currTextHeight <= contHeight) { - let wordIndex = 0; - let currentRowWidth = 0; - let wordsInCurrRow = 0; - rowsCount = 1; - - while (wordIndex < words.length) { - const word = words[wordIndex]; - const wordWidth = word.length * currFontSize * 0.5; - //console.log(wordWidth) - - if (currentRowWidth + wordWidth <= contWidth) { - currentRowWidth += wordWidth; - ++wordsInCurrRow; - } else { - if (words.length !== 1 && words.length > wordsInCurrRow) { - rowsCount++; - currentRowWidth = wordWidth; - wordsInCurrRow = 1; - } else { - break; - } - } - - wordIndex++; - } - - currTextHeight = rowsCount * currFontSize * 2; - //console.log(rowsCount, currFontSize, currTextHeight) - - currFontSize += 1; - } - - return currFontSize - 1; - }; - - private static getDimensions = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number): { width: number; height: number; coord: { x: number; y: number } } => { - const l = (coords.tl[0] * parentHeight) / 2; - const t = coords.tl[1] * parentWidth / 2; //prettier-ignore - const r = (coords.br[0] * parentHeight) / 2; - const b = coords.br[1] * parentWidth / 2; //prettier-ignore - const width = r - l; - const height = b - t; - const coord = { x: l, y: t }; - //console.log(coords, parentWidth, parentHeight, height); - return { width, height, coord }; - }; - - public static FreeformField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, content: string, opts: FieldOpts) => { - const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight); - - const docWithBasicOpts = Docs.Create.FreeformDocument([], { - isDefaultTemplateDoc: true, - _height: height, - _width: width, - title: title, - x: coord.x, - y: coord.y, - backgroundColor: opts.backgroundColor ?? '', - _layout_borderRounding: `${opts.cornerRounding ?? 0}px`, - borderColor: opts.borderColor, - borderWidth: opts.borderWidth, - opacity: opts.opacity, - hCentering: opts.contentXCentering, - _rotation: opts.rotation, - }); - - return docWithBasicOpts; - }; - - public static TextField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, content: string, opts: FieldOpts) => { - const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight); - - const docWithBasicOpts = Docs.Create.TextDocument(content, { - isDefaultTemplateDoc: true, - _height: height, - _width: width, - title: title, - x: coord.x, - y: coord.y, - _text_fontSize: `${FieldUtils.calculateFontSize(width, height, content, true)}`, - backgroundColor: opts.backgroundColor ?? '', - text_fontColor: opts.color, - contentBold: opts.fontBold, - textTransform: opts.fontTransform, - color: opts.color, - _layout_borderRounding: `${opts.cornerRounding ?? 0}px`, - borderColor: opts.borderColor, - borderWidth: opts.borderWidth, - opacity: opts.opacity, - hCentering: opts.contentXCentering, - _rotation: opts.rotation, - }); - - docWithBasicOpts._layout_hideScroll = true; - - return docWithBasicOpts; - }; - - public static ImageField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, content: string, opts: FieldOpts) => { - const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight); - - const doc = Docs.Create.ImageDocument(content, { - isDefaultTemplateDoc: true, - _height: height, - _width: width, - title: title, - x: coord.x, - y: coord.y, - _layout_fitWidth: false, - backgroundColor: opts.backgroundColor ?? '', - _layout_borderRounding: `${opts.cornerRounding ?? 0}px`, - borderColor: opts.borderColor, - borderWidth: opts.borderWidth, - opacity: opts.opacity, - _rotation: opts.rotation, - }); - - //setTimeout(() => {doc._height = height; doc._width = width}, 10); - - return doc; - }; - - public static CarouselField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, fields: Doc[]) => { - const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight); - - const doc = Docs.Create.Carousel3DDocument(fields, { _height: height, _width: width, title: title, x: coord.x, y: coord.y, _text_fontSize: `${height / 2}` }); - - return doc; - }; -} |