aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNathan-SR <144961007+Nathan-SR@users.noreply.github.com>2025-05-11 16:52:17 -0400
committerNathan-SR <144961007+Nathan-SR@users.noreply.github.com>2025-05-11 16:52:17 -0400
commite62f51bacace3d91f388202135426445721097cc (patch)
tree0b652d55e6c055b63c43feba7a41d1bab28af005 /src
parent4af498433a887c70dc7043a5a34eef7fff5bbbe0 (diff)
menubutton component
Diffstat (limited to 'src')
-rw-r--r--src/client/apis/gpt/GPT.ts4
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.scss34
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx32
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/DocCreatorMenuButton.tsx41
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx90
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewGrid.tsx8
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts2
7 files changed, 122 insertions, 89 deletions
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index d7b378958..455551f40 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -107,13 +107,13 @@ const callTypeMap: { [type in GPTCallType]: GPTCallOpts } = {
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-4-turbo',
+ model: 'gpt-4.1',
maxTokens: 512,
temp: 0.5,
prompt: 'Your job is to provide brief descriptions for columns in a dataset based on example rows. Your descriptions should be geared towards how each column’s data might fit together into a visual template. Would they make good titles, main focuses, captions, descriptions, etc. Pay special attention to connections between columns, i.e. is there one column that specifically seems to describe/be related to another more than the rest? You should provide your analysis in JSON format like so: {“col1”:”description”, “col2”:”description”, …}. DO NOT INCLUDE ANY OTHER TEXT, ONLY THE JSON.',
},
vizsum2: {
- model: 'gpt-4-turbo',
+ model: 'gpt-4.1',
maxTokens: 512,
temp: 0.5,
prompt: 'Your job is to provide structured information on columns in a dataset based on example rows. You will categorize each column in two ways: by type and size. The size categories are as follows: tiny (one or two words), small (a sentence/multiple words), medium (a few sentences), large (a longer paragraph), and huge (a very long or multiple paragraphs). The type categories are as follows: visual (links/file paths to images, pdfs, maps, or any other visual media type), and text (plain text that isn’t a link/file path). Visual media should be assumed to have size “medium” “large” or “huge”. You will give your responses in JSON format, like so: {“title (of column)”:{“type”:”text”, “size”:”small”}, “title (of column)”:{“type”:”visual”, “size”:”medium”}, …}. DO NOT INCLUDE ANY OTHER TEXT, ONLY THE JSON.',
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.scss b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.scss
index 9fb973265..2a1a79029 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.scss
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.scss
@@ -27,7 +27,6 @@
background: whitesmoke;
background-color: rgb(50, 50, 50);
border-radius: 5px;
- border: 1px solid rgb(180, 180, 180);
padding: 0px;
font-size: 13px;
//box-shadow: 3px 3px rgb(29, 29, 31);
@@ -35,6 +34,24 @@
&:hover {
box-shadow: none;
}
+
+ &.no-margin {
+ margin: 0px;
+ }
+
+ &.border {
+ border: 1px solid rgb(180, 180, 180);
+ }
+
+ &.float-right {
+ float: right;
+ margin-left: auto;
+ }
+
+ &.absolute-right {
+ position: absolute;
+ right: 0px;
+ }
&.right{
margin-left: 0px;
@@ -403,6 +420,8 @@
margin-left: 5%;
background-color: rgb(50, 50, 50);
border-radius: 5px;
+ overflow: hidden;
+ resize: none;
}
.docCreatorMenu-variations-tab {
@@ -459,19 +478,6 @@
width: 100%;
}
-.section-reveal-options {
- margin-top: 0px;
- margin-bottom: 0px;
- margin-right: 0px;
- margin-left: auto;
- border: 0px;
- background: none;
-
- &.float-right {
- float: right;
- }
-}
-
.docCreatorMenu-templates-displays {
display: flex;
flex-direction: column;
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx
index 8a98399b6..2e4b81253 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx
@@ -37,6 +37,7 @@ import { StaticContentField } from './TemplateFieldTypes/StaticContentField';
import { TemplateMenuAIUtils } from './Backend/TemplateMenuAIUtils'
import { TemplatePreviewGrid } from './Menu/TemplatePreviewGrid';
import { TemplateEditingWindow } from './Menu/TemplateEditingWindow';
+import { DocCreatorMenuButton } from './Menu/DocCreatorMenuButton';
export enum LayoutType {
FREEFORM = 'Freeform',
@@ -983,9 +984,7 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps>
}
}))}>
<span className="field-title">{`${field.title} Field`}</span>
- <button className="docCreatorMenu-menu-button section-reveal-options no-margin" onPointerDown={e => this.setUpButtonClick(e, () => this.removeField(field))} style={{ position: 'absolute', right: '0px' }}>
- <FontAwesomeIcon icon="minus" />
- </button>
+ <DocCreatorMenuButton icon={'minus'} styles={'no-margin absolute-right'} function={() => this.removeField(field)}/>
</div>
{ this._collapsedCols.includes(field.title) ? null :
<>
@@ -1042,26 +1041,16 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps>
return (
<div className="docCreatorMenu-dashboard-view">
<div className="topbar">
- <button className="docCreatorMenu-menu-button section-reveal-options" onPointerDown={e => this.setUpButtonClick(e, this.addField)}>
- <FontAwesomeIcon icon="plus" />
- </button>
- <button className="docCreatorMenu-menu-button section-reveal-options float-right" onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => (this._menuContent = 'templates')))}>
- <FontAwesomeIcon icon="arrow-left" />
- </button>
+ <DocCreatorMenuButton icon={'plus'} function={this.addField}/>
+ <DocCreatorMenuButton icon={'arrow-left'} styles={'float-right'} function={() => runInAction(() => (this._menuContent = 'templates'))}/>
</div>
<div className="panels-container">{this.fieldsInfos.map((field, i) => fieldPanel(field, i))}</div>
</div>
);
}
- @computed get editingView() {
- return <TemplateEditingWindow
- setupButtonClick={this.setUpButtonClick}
- template={this._currEditingTemplate as Template}
- menu={this}
- />
- }
-
+ @computed get editingView() { return <TemplateEditingWindow template={this._currEditingTemplate as Template} menu={this} /> }
+
get renderSelectedViewType() {
switch (this._menuContent) {
case 'templates':
@@ -1073,14 +1062,11 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps>
menu={this}
loading={this._GPTLoading}
optionsButtonOpts={['gear', () => (this._menuContent = 'dashboard')]}
- setupButtonClick={this.setUpButtonClick}
templates={this._suggestedTemplates}
/>
<div className="docCreatorMenu-GPT-options">
<div className="docCreatorMenu-GPT-options-container">
- <button className="docCreatorMenu-menu-button" onPointerDown={e => this.setUpButtonClick(e, () => this.generatePresetTemplates())}>
- <FontAwesomeIcon icon="arrows-rotate" />
- </button>
+ <DocCreatorMenuButton icon={'arrows-rotate'} styles={'border'} function={this.generatePresetTemplates}/>
</div>
</div>
</div>
@@ -1166,9 +1152,7 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps>
{topButton('magnifying-glass', 'options', onOptionsSelected, 'middle')}
{topButton('bars', 'saved', onSavedSelected, 'right')}
</div>
- <button className="docCreatorMenu-menu-button close-menu" onPointerDown={e => this.setUpButtonClick(e, this.closeMenu)}>
- <FontAwesomeIcon icon="minus" />
- </button>
+ <DocCreatorMenuButton icon={'minus'} styles={'float-right'} function={this.closeMenu}/>
</div>
{this.renderSelectedViewType}
</div>
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/DocCreatorMenuButton.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/DocCreatorMenuButton.tsx
new file mode 100644
index 000000000..1d8139d40
--- /dev/null
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/DocCreatorMenuButton.tsx
@@ -0,0 +1,41 @@
+import { IconProp } from "@fortawesome/fontawesome-svg-core";
+import { ObservableReactComponent } from "../../../../ObservableReactComponent";
+import React from "react";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { setupMoveUpEvents, returnFalse } from "../../../../../../ClientUtils";
+import { emptyFunction } from "../../../../../../Utils";
+import { undoable } from "../../../../../util/UndoManager";
+import { observer } from "mobx-react";
+
+interface DocCreatorMenuButtonProps {
+ icon: IconProp;
+ function: () => any;
+ styles?: string;
+}
+
+@observer
+export class DocCreatorMenuButton extends ObservableReactComponent<DocCreatorMenuButtonProps> {
+
+ setupButtonClick = (e: React.PointerEvent, func: (...args: any) => void) => {
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ emptyFunction,
+ undoable(clickEv => {
+ clickEv.stopPropagation();
+ clickEv.preventDefault();
+ func();
+ }, 'create docs')
+ );
+ };
+
+ render() {
+
+ return (
+ <button className={`docCreatorMenu-menu-button ${this._props.styles}`} onPointerDown={e => this.setupButtonClick(e, async () => this._props.function())}>
+ <FontAwesomeIcon icon={this._props.icon} />
+ </button>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx
index df6b791c7..0434d0ccb 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx
@@ -15,11 +15,11 @@ import { TemplateMenuAIUtils } from "../Backend/TemplateMenuAIUtils";
import { ObservableReactComponent } from "../../../../ObservableReactComponent";
import { IDisposer } from "mobx-utils";
import { ImageField } from "../../../../../../fields/URLField";
+import { DocCreatorMenuButton } from "./DocCreatorMenuButton";
interface TemplateEditingWindowProps {
menu: DocCreatorMenu;
template: Template;
- setupButtonClick: (e: React.PointerEvent, func: () => void) => void;
}
@observer
@@ -27,7 +27,8 @@ export class TemplateEditingWindow extends ObservableReactComponent<TemplateEdit
private fireflyPrompt: string = 'Use this template to generate an empty baseball card template.';
private previewWindow: HTMLDivElement | null = null;
- private _disposers: { [name: string]: IDisposer } = {};
+ private disposers: { [name: string]: IDisposer } = {};
+ private promptInput: HTMLTextAreaElement | null = null;
@observable _loading: boolean = false;
@observable _variationsTabOpen: boolean = false;
@@ -35,7 +36,7 @@ export class TemplateEditingWindow extends ObservableReactComponent<TemplateEdit
@observable _variations: Template[] = [];
componentDidMount(): void {
- this._disposers.windowDimensions = reaction(() =>
+ this.disposers.windowDimensions = reaction(() =>
this._props.menu._resizing,
() => { this.forceUpdate() },
{ fireImmediately: true }
@@ -43,7 +44,12 @@ export class TemplateEditingWindow extends ObservableReactComponent<TemplateEdit
}
componentWillUnmount() {
- Object.values(this._disposers).forEach(disposer => disposer?.());
+ Object.values(this.disposers).forEach(disposer => disposer?.());
+ }
+
+ setPromptInputRef: React.LegacyRef<HTMLTextAreaElement> = (node) => {
+ this.promptInput = node;
+ this.forceUpdate();
}
setContainerRef: React.LegacyRef<HTMLDivElement> = (node) => {
@@ -58,6 +64,24 @@ export class TemplateEditingWindow extends ObservableReactComponent<TemplateEdit
} else if (this.previewWindow && !open) {
this.previewWindow.style.height = String(Number(this.previewWindow.clientHeight) * 5/3);
}
+ this.forceUpdate();
+ }
+
+ generateVariations = async () => {
+ this._props.menu._variations = [];
+ this._loading = true;
+ this._variationURLs = await this._props.menu.generateVariations(this._props.template.clone(false).getRenderedDoc()!, this.fireflyPrompt);
+ this._variationURLs.forEach(url => {
+ const newTemplate: Template = this._props.template.clone(true);
+ this._props.menu._variations.push(newTemplate);
+ });
+ this._loading = false;
+ setTimeout(() => {
+ this._variationURLs.forEach((url, i) => {
+ this._props.menu._variations[i].setImageAsBackground(url, true);
+ });
+ this.forceUpdate();
+ });
}
get fireflyVariationsTab() {
@@ -71,37 +95,27 @@ export class TemplateEditingWindow extends ObservableReactComponent<TemplateEdit
loading={this._loading}
templates={this._props.menu._variations}
optionsButtonOpts={['gear', () => {}]}
- setupButtonClick={this._props.setupButtonClick}
previewBoxRightButtonOpts={['gear', (template: Template) => {this.forceUpdate();}]}
/>
<div className="docCreatorMenu-section">
<div className="docCreatorMenu-variation-prompt-input">
<textarea
className="docCreatorMenu-variation-prompt-input-textbox"
+ ref={this.setPromptInputRef}
onChange={e => this.fireflyPrompt = e.target.value}
+ onInput={() => {
+ if (this.promptInput !== null) {
+ this.promptInput.style.height = 'auto';
+ this.promptInput.style.height = this.promptInput.scrollHeight + 'px';
+ }
+ }}
defaultValue={''}
placeholder={'Enter a custom prompt here (optional)'}
/>
- <button className="docCreatorMenu-menu-button"
- onPointerDown={e => this._props.setupButtonClick(e, async () => {
- this._props.menu._variations = [];
- this._loading = true;
- this._variationURLs = await this._props.menu.generateVariations(this._props.template.clone(false).getRenderedDoc()!, this.fireflyPrompt);
- this._variationURLs.forEach(url => {
- const newTemplate: Template = this._props.template.clone(true);
- this._props.menu._variations.push(newTemplate);
- });
- this._loading = false;
- setTimeout(() => {
- this._variationURLs.forEach((url, i) => {
- this._props.menu._variations[i].setImageAsBackground(url, true);
- });
- this.forceUpdate();
- });
- })
- }>
- <FontAwesomeIcon icon="arrows-rotate" />
- </button>
+ <DocCreatorMenuButton icon={'arrows-rotate'} styles={'border'} function={this.generateVariations}/>
+ </div>
+ <div className='docCreatorMenu-variation-options-container'>
+
</div>
</div>
</>
@@ -146,25 +160,13 @@ export class TemplateEditingWindow extends ObservableReactComponent<TemplateEdit
{this.renderedDocPreview}
{this._variationsTabOpen ? this.fireflyVariationsTab : null}
<div className="right-buttons-panel">
- <button
- className="docCreatorMenu-menu-button section-reveal-options top-right"
- onPointerDown={e =>
- this._props.setupButtonClick(e, () => {
- if (this._props.template === this._props.menu._selectedTemplate) {
- this._props.menu.updateRenderedPreviewCollection(this._props.template);
- }
- this._props.menu.setExpandedView(undefined);
- })
- }>
- <FontAwesomeIcon icon="minimize" />
- </button>
- <button className="docCreatorMenu-menu-button section-reveal-options top-right" onPointerDown={e => this._props.setupButtonClick(e, async () => {
- this.setVariationTab(!this._variationsTabOpen);
- // this._props.template.setImageAsBackground('https://www.onthegotours.com/repository/Great-Wall-New-Pic-228551391689622.jpg', true);
- this.forceUpdate();
- })}>
- <FontAwesomeIcon icon="lightbulb" />
- </button>
+ <DocCreatorMenuButton icon={'minimize'} function={() => {
+ if (this._props.template === this._props.menu._selectedTemplate) {
+ this._props.menu.updateRenderedPreviewCollection(this._props.template);
+ }
+ this._props.menu.setExpandedView(undefined);
+ }}/>
+ <DocCreatorMenuButton icon={'lightbulb'} function={() => this.setVariationTab(!this._variationsTabOpen)}/>
</div>
</div>
</div>
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewGrid.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewGrid.tsx
index e78109b62..84ca6546d 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewGrid.tsx
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewGrid.tsx
@@ -11,6 +11,7 @@ import { observer } from "mobx-react";
import { DocCreatorMenu } from "../DocCreatorMenu";
import { TemplatePreviewBox } from "./TemplatePreviewBox";
import { IconProp } from "@fortawesome/fontawesome-svg-core";
+import { DocCreatorMenuButton } from "./DocCreatorMenuButton";
export interface SuggestedTemplatesProps {
menu: DocCreatorMenu;
@@ -20,7 +21,6 @@ export interface SuggestedTemplatesProps {
optionsButtonOpts?: [IconProp, (...args: any) => any];
previewBoxLeftButtonOpts?: [IconProp, (...args: any) => any];
previewBoxRightButtonOpts?: [IconProp, (...args: any) => any];
- setupButtonClick: (e: React.PointerEvent, func: () => void) => void;
}
@observer
@@ -31,9 +31,9 @@ export class TemplatePreviewGrid extends ObservableReactComponent<SuggestedTempl
<div className="docCreatorMenu-section">
<div className="docCreatorMenu-section-topbar">
<div className="docCreatorMenu-section-title">{this.props.title}</div>
- {this._props.optionsButtonOpts ? (<button className="docCreatorMenu-menu-button section-reveal-options" onPointerDown={e => this.props.setupButtonClick(e, () => runInAction(this._props.optionsButtonOpts![1]))}>
- <FontAwesomeIcon icon={this._props.optionsButtonOpts[0] as IconProp} />
- </button>) : null}
+ {this._props.optionsButtonOpts ?
+ <DocCreatorMenuButton icon={this._props.optionsButtonOpts[0] as IconProp} styles={'float-right'} function={() => runInAction(this._props.optionsButtonOpts![1])}/>
+ : null}
</div>
<div className="docCreatorMenu-templates-preview-window">
{this._props.loading ?
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts
index 10724c9f7..7ff521f18 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts
@@ -81,7 +81,7 @@ export class Template {
isValidTemplate = (cols: Col[]) => {
const maxMatches = this.maxMatches(this.getMatches(cols));
- return maxMatches === this.contentFields.length;
+ return maxMatches === this.contentFields.length && this.title !== 'template_framework';
};
applyConditionalLogicToField = (field: TemplateField) => {