aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSophie Zhang <sophie_zhang@brown.edu>2023-11-02 13:10:48 -0400
committerSophie Zhang <sophie_zhang@brown.edu>2023-11-02 13:10:48 -0400
commit8fccdb8c21015eb9204de7c24a80ece82f338d8e (patch)
tree4b54625dc50c1ecba466c8e02f1fe2acc0cce165
parenta1d00a36ef1afa97198a825bd25ebb4c5e598848 (diff)
gpt stylization
-rw-r--r--src/client/apis/gpt/customization.ts51
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx51
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx2
-rw-r--r--src/client/views/nodes/trails/PresElementBox.tsx24
4 files changed, 99 insertions, 29 deletions
diff --git a/src/client/apis/gpt/customization.ts b/src/client/apis/gpt/customization.ts
index 3d9a0ead3..20dac0a4e 100644
--- a/src/client/apis/gpt/customization.ts
+++ b/src/client/apis/gpt/customization.ts
@@ -16,6 +16,10 @@ const prompts: { [key: string]: PromptInfo } = {
},
};
+export const addCustomizationProperty = (type: CustomizationType, name: string, description: string, values?: string[]) => {
+ values ? prompts[type].features.push({ name, description, values }) : prompts[type].features.push({ name, description });
+};
+
const setupPresSlideCustomization = () => {
addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'title', 'is the title/name of the slide.');
addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'presentation_transition', 'is a number in milliseconds for how long it should take to transition/move to a slide.');
@@ -24,10 +28,6 @@ const setupPresSlideCustomization = () => {
setupPresSlideCustomization();
-export const addCustomizationProperty = (type: CustomizationType, name: string, description: string, values?: string[]) => {
- values ? prompts[type].features.push({ name, description, values }) : prompts[type].features.push({ name, description });
-};
-
export const gptTrailSlideCustomization = async (inputText: string) => {
let prompt = prompts.trails.description;
@@ -59,3 +59,46 @@ export const gptTrailSlideCustomization = async (inputText: string) => {
return 'Error connecting with API.';
}
};
+
+// palette / styling
+export const generatePalette = async (inputData: any) => {
+ let prompt = 'Dash is a hypermedia web application that allows users to organize documents of different media types into collections. I want you to come up with a cohesive color palette for a collection.';
+ prompt +=
+ 'I am going to give you a json object of this format:' +
+ JSON.stringify({ collectionDescription: 'string', documents: 'Document[]' }) +
+ '. collectionDescription is the title of the collection, which you should create a color palette based on. This is the document format:' +
+ JSON.stringify({
+ id: 'number',
+ textSize: 'number',
+ textContent: 'string',
+ }) +
+ '. You are going to create a color palette based mostly on collectionDescription, and loosely on the text content and text size of the documents. Return a json object in this format:' +
+ JSON.stringify({
+ collectionBackgroundColor: 'string',
+ documentsWithColors: 'DocumentWithColor[]',
+ }) +
+ '. collectionBackgroundColor, should be a string hex value for the background color of the collection. documentsWithColors has the same length and order of the input documents. DocumentWithColor has this format:' +
+ JSON.stringify({
+ id: 'number',
+ color: 'string',
+ }) +
+ ", and each element’s color is based on the theme of the overall color palette and also by its document’s textContent. Please pay attention to aesthetics of how each document's color complement the background and each other and choose a variety of colors when appropriate. Respond with only the JSON object.";
+
+ // console.log('Prompt', prompt);
+ try {
+ const response = await openai.createChatCompletion({
+ model: 'gpt-4',
+ messages: [
+ { role: 'system', content: prompt },
+ { role: 'user', content: JSON.stringify(inputData) },
+ ],
+ temperature: 0.1,
+ max_tokens: 2000,
+ });
+ console.log(response.data.choices[0].message?.content);
+ return response.data.choices[0].message?.content;
+ } catch (err) {
+ console.log(err);
+ return 'Error connecting with API.';
+ }
+};
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 0c3033579..002ebf1ae 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -52,6 +52,7 @@ import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCurso
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
import React = require('react');
+import { generatePalette } from '../../../apis/gpt/customization';
export type collectionFreeformViewProps = {
NativeWidth?: () => number;
@@ -1573,6 +1574,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
componentDidMount() {
+ this.printDoc(this.rootDoc);
this.props.setContentView?.(this);
super.componentDidMount?.();
this.props.setBrushViewer?.(this.brushView);
@@ -1787,6 +1789,53 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
};
+ printDoc = (doc: Doc) => {
+ console.log('Printing keys');
+ Object.keys(doc).forEach(key => {
+ console.log(key, ':', doc[key]);
+ });
+ };
+
+ // gpt styling
+ @action
+ gptStyling = async () => {
+ console.log('Title', this.rootDoc.title);
+ console.log('bgcolor', this.layoutDoc._backgroundColor);
+ // doc.backgroundColor
+ console.log('styling');
+ const inputDocs = this.childDocs.filter(doc => doc.type == 'rich text');
+ const gptInput = inputDocs.map((doc, i) => ({
+ id: i,
+ textContent: (doc.text as RichTextField)?.Text,
+ textSize: 16,
+ }));
+
+ // inputDocs[0].backgroundColor = '#3392ff';
+
+ const collectionDescription = StrCast(this.rootDoc.title);
+
+ console.log({
+ collectionDescription,
+ documents: gptInput,
+ });
+
+ try {
+ const res = await generatePalette({
+ collectionDescription,
+ documents: gptInput,
+ });
+ console.log('done');
+ if (typeof res === 'string') {
+ const resObj = JSON.parse(res);
+ console.log('Result ', resObj);
+ this.rootDoc.backgroundColor = resObj.collectionBackgroundColor;
+ (resObj.documentsWithColors as any[]).forEach((elem, i) => (inputDocs[i].backgroundColor = elem.color));
+ }
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
onContextMenu = (e: React.MouseEvent) => {
if (this.props.isAnnotationOverlay || !ContextMenu.Instance) return;
@@ -1826,6 +1875,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
optionItems.push({ description: (this._showAnimTimeline ? 'Close' : 'Open') + ' Animation Timeline', event: action(() => (this._showAnimTimeline = !this._showAnimTimeline)), icon: 'eye' });
this.props.renderDepth && optionItems.push({ description: 'Use Background Color as Default', event: () => (Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor)), icon: 'palette' });
this.props.renderDepth && optionItems.push({ description: 'Fit Content Once', event: this.fitContentOnce, icon: 'object-group' });
+ // gpt styling
+ this.props.renderDepth && optionItems.push({ description: 'Style with AI', event: this.gptStyling, icon: 'paint-brush' });
if (!Doc.noviceMode) {
optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? 'Freeze' : 'Unfreeze') + ' Aspect', event: this.toggleNativeDimensions, icon: 'snowflake' });
}
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 87ffb0112..19f271246 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -40,10 +40,10 @@ import ReactLoading from 'react-loading';
import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
import { IconButton, Type } from 'browndash-components';
import { AiOutlineSend } from 'react-icons/ai';
-import { gptTrailSlideCustomization } from '../../../apis/gpt/GPT';
import { DictationManager } from '../../../util/DictationManager';
import { BiMicrophone, BiX } from 'react-icons/bi';
import TextareaAutosize from 'react-textarea-autosize';
+import { gptTrailSlideCustomization } from '../../../apis/gpt/customization';
export interface pinDataTypes {
scrollable?: boolean;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 482d38f59..37f449001 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -26,10 +26,6 @@ import './PresElementBox.scss';
import { PresMovement } from './PresEnums';
import React = require('react');
import { TreeView } from '../../collections/TreeView';
-import { BranchingTrailManager } from '../../../util/BranchingTrailManager';
-import { MultiToggle, Type } from 'browndash-components';
-import { gptTrailSlideCustomization } from '../../../apis/gpt/GPT';
-import { DictationManager } from '../../../util/DictationManager';
/**
* This class models the view a document added to presentation will have in the presentation.
* It involves some functionality for its buttons and options.
@@ -438,25 +434,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
// GPT
- @action
- customizeWithGPT = async (input: string) => {
- const testInput = 'change title to Customized Slide, transition for 2.3s with fade in effect';
-
- try {
- const res = await gptTrailSlideCustomization(testInput);
- if (typeof res === 'string') {
- const resObj = JSON.parse(res);
- console.log('Result ', resObj);
-
- for (let key in resObj) {
- this.rootDoc[key] = resObj[key];
- }
- }
- } catch (err) {
- console.error(err);
- }
- };
-
@computed get presButtons() {
const presBox = this.presBox;
const presBoxColor = StrCast(presBox?._backgroundColor);
@@ -568,7 +545,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
PresBox.Instance.setChatActive(true);
PresBox.Instance.slideToModify = this.rootDoc;
PresBox.Instance.recordDictation();
- // this.customizeWithGPT('');
}}>
<FontAwesomeIcon icon={'message'} onPointerDown={e => e.stopPropagation()} />
</div>