aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-11-22 10:27:33 -0500
committerbobzel <zzzman@gmail.com>2024-11-22 10:27:33 -0500
commit89424e0a8efc6cf3364a2fd1ffc85c9d0d837453 (patch)
tree9f0bb770707b2e4239c0618d7435976bcc1c0f16
parent7b38bbc4d845fa524e8310a0ec05b0e776b47c82 (diff)
added initial Firefly endpoint and hanged smartDrawHandler to generate an image and an svg.
-rw-r--r--src/client/util/bezierFit.ts3
-rw-r--r--src/client/views/MainView.tsx32
-rw-r--r--src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx3
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx9
-rw-r--r--src/client/views/smartdraw/SmartDrawHandler.tsx29
-rw-r--r--src/server/ApiManagers/DataVizManager.ts2
-rw-r--r--src/server/ApiManagers/FireflyManager.ts51
-rw-r--r--src/server/DashUploadUtils.ts3
-rw-r--r--src/server/index.ts3
-rw-r--r--webpack.config.js7
10 files changed, 113 insertions, 29 deletions
diff --git a/src/client/util/bezierFit.ts b/src/client/util/bezierFit.ts
index d52460023..84b27e84c 100644
--- a/src/client/util/bezierFit.ts
+++ b/src/client/util/bezierFit.ts
@@ -703,7 +703,6 @@ export function SVGToBezier(name: SVGType, attributes: any): Point[] {
coordList.push({ X: parseInt(match[1]), Y: parseInt(match[2]) });
coordList.push({ X: parseInt(match[1]), Y: parseInt(match[2]) });
coordList.push({ X: parseInt(match[3]), Y: parseInt(match[4]) });
- coordList.push({ X: parseInt(match[3]), Y: parseInt(match[4]) });
lastPt = { X: parseInt(match[3]), Y: parseInt(match[4]) };
} else if (match[0].startsWith('C')) {
coordList.push({ X: parseInt(match[5]), Y: parseInt(match[6]) });
@@ -720,7 +719,7 @@ export function SVGToBezier(name: SVGType, attributes: any): Point[] {
}
});
const hasZ = attributes.d.match(/Z/);
- if (hasZ) {
+ if (hasZ || attributes.fill) {
coordList.push(lastPt);
coordList.push(startPt);
coordList.push(startPt);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7779d339f..0d071fe4f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -8,7 +8,7 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import ResizeObserver from 'resize-observer-polyfill';
import '../../../node_modules/browndash-components/dist/styles/global.min.css';
-import { ClientUtils, lightOrDark, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../ClientUtils';
+import { ClientUtils, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../ClientUtils';
import { emptyFunction } from '../../Utils';
import { Doc, DocListCast, GetDocFromUrl, Opt, returnEmptyDoclist } from '../../fields/Doc';
import { DocData } from '../../fields/DocSymbols';
@@ -1023,10 +1023,36 @@ export class MainView extends ObservableReactComponent<object> {
<svg style={{ width: '100%', height: '100%' }}>
{[
...SnappingManager.HorizSnapLines.map(l => (
- <line key={'horiz' + l} x1="0" y1={l} x2="2000" y2={l} stroke={lightOrDark(StrCast(dragPar.layoutDoc.backgroundColor, 'gray'))} opacity={0.3} strokeWidth={1} strokeDasharray="2 2" />
+ <line
+ key={'horiz' + l}
+ x1="0"
+ y1={l}
+ x2="2000"
+ y2={l}
+ stroke={
+ SnappingManager.userVariantColor
+ /* lightOrDark(StrCast(dragPar.layoutDoc.backgroundColor, 'gray'))*/
+ }
+ opacity={0.3}
+ strokeWidth={3}
+ strokeDasharray="2 2"
+ />
)),
...SnappingManager.VertSnapLines.map(l => (
- <line key={'vert' + l} y1={this.topOfMainDocContent.toString()} x1={l} y2="2000" x2={l} stroke={lightOrDark(StrCast(dragPar.layoutDoc.backgroundColor, 'gray'))} opacity={0.3} strokeWidth={1} strokeDasharray="2 2" />
+ <line
+ key={'vert' + l}
+ y1={this.topOfMainDocContent.toString()}
+ x1={l}
+ y2="2000"
+ x2={l}
+ stroke={
+ SnappingManager.userVariantColor
+ /* lightOrDark(StrCast(dragPar.layoutDoc.backgroundColor, 'gray'))*/
+ }
+ opacity={0.3}
+ strokeWidth={3}
+ strokeDasharray="2 2"
+ />
)),
]}
</svg>
diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
index a61705250..3ef6bdd8b 100644
--- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
+++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
@@ -431,7 +431,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
doc = Docs.Create.FunctionPlotDocument([], options);
break;
case 'dataviz':
- case 'data_viz':
+ case 'data_viz': {
const { fileUrl, id } = await Networking.PostToServer('/createCSV', {
filename: (options.title as string).replace(/\s+/g, '') + '.csv',
data: data,
@@ -439,6 +439,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
doc = Docs.Create.DataVizDocument(fileUrl, { ...options, text: RTFCast(data) });
this.addCSVForAnalysis(doc, id);
break;
+ }
case 'chat':
doc = Docs.Create.ChatDocument(options);
break;
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 5ab9b556c..fe03f32a5 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -131,12 +131,15 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
/**
* Creates a GPT drawing based on selected text.
*/
- gptDraw = async (e: React.PointerEvent) => {
+ gptDraw = (e: React.PointerEvent) => {
try {
SmartDrawHandler.Instance.AddDrawing = this.createDrawingAnnotation;
runInAction(() => (this._isLoading = true));
- await SmartDrawHandler.Instance.drawWithGPT({ X: e.clientX, Y: e.clientY }, this._selectedText, 5, 100, true);
- runInAction(() => (this._isLoading = false));
+ SmartDrawHandler.Instance.drawWithGPT({ X: e.clientX, Y: e.clientY }, this._selectedText, 5, 100, true)?.then(
+ action(() => {
+ this._isLoading = false;
+ })
+ );
} catch (err) {
console.error(err);
}
diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx
index d0f6566a5..342b91bd9 100644
--- a/src/client/views/smartdraw/SmartDrawHandler.tsx
+++ b/src/client/views/smartdraw/SmartDrawHandler.tsx
@@ -13,6 +13,7 @@ import { Doc, DocListCast } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { InkData, InkField, InkTool } from '../../../fields/InkField';
import { BoolCast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
+import { Networking } from '../../Network';
import { GPTCallType, gptAPICall, gptDrawingColor } from '../../apis/gpt/GPT';
import { Docs } from '../../documents/Documents';
import { SettingsManager } from '../../util/SettingsManager';
@@ -21,7 +22,8 @@ import { SVGToBezier, SVGType } from '../../util/bezierFit';
import { InkingStroke } from '../InkingStroke';
import { ObservableReactComponent } from '../ObservableReactComponent';
import { MarqueeView } from '../collections/collectionFreeForm';
-import { ActiveInkArrowEnd, ActiveInkArrowStart, ActiveInkDash, ActiveInkFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, DocumentView } from '../nodes/DocumentView';
+import { ActiveInkArrowEnd, ActiveInkArrowStart, ActiveInkBezierApprox, ActiveInkColor, ActiveInkDash, ActiveInkFillColor, ActiveInkWidth, ActiveIsInkMask, DocumentView, DocumentViewInternal } from '../nodes/DocumentView';
+import { OpenWhere } from '../nodes/OpenWhere';
import './SmartDrawHandler.scss';
export interface DrawingOptions {
@@ -230,20 +232,21 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
* Calls GPT API to create a drawing based on user input.
*/
@action
- drawWithGPT = async (startPt: { X: number; Y: number }, input: string, complexity: number, size: number, autoColor: boolean) => {
- if (input === '') return;
- this._lastInput = { text: input, complexity: complexity, size: size, autoColor: autoColor, x: startPt.X, y: startPt.Y };
- const res = await gptAPICall(`"${input}", "${complexity}", "${size}"`, GPTCallType.DRAW, undefined, true);
- if (!res) {
- console.error('GPT call failed');
- return;
- }
- const strokeData = await this.parseSvg(res, startPt, false, autoColor);
- const drawingDoc = strokeData && this.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes);
- drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res);
+ drawWithGPT = (startPt: { X: number; Y: number }, prompt: string, complexity: number, size: number, autoColor: boolean) => {
+ if (prompt === '') return;
+ this._lastInput = { text: prompt, complexity: complexity, size: size, autoColor: autoColor, x: startPt.X, y: startPt.Y };
+
+ Networking.PostToServer('/queryFireflyImage', { prompt }).then(img => DocumentViewInternal.addDocTabFunc(Docs.Create.ImageDocument(img, { title: prompt }), OpenWhere.addRight));
+
+ const result = gptAPICall(`"${prompt}", "${complexity}", "${size}"`, GPTCallType.DRAW, undefined, true).then(res =>
+ this.parseSvg(res, startPt, false, autoColor).then(strokeData => {
+ const drawingDoc = strokeData && this.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes);
+ drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res);
+ })
+ );
this._errorOccurredOnce = false;
- return strokeData;
+ return result;
};
/**
diff --git a/src/server/ApiManagers/DataVizManager.ts b/src/server/ApiManagers/DataVizManager.ts
index 88f22992d..d2028f23b 100644
--- a/src/server/ApiManagers/DataVizManager.ts
+++ b/src/server/ApiManagers/DataVizManager.ts
@@ -9,7 +9,7 @@ export default class DataVizManager extends ApiManager {
register({
method: Method.GET,
subscription: '/csvData',
- secureHandler: async ({ req, res }) => {
+ secureHandler: ({ req, res }) => {
const uri = req.query.uri as string;
return new Promise<void>(resolve => {
diff --git a/src/server/ApiManagers/FireflyManager.ts b/src/server/ApiManagers/FireflyManager.ts
new file mode 100644
index 000000000..04fa8f065
--- /dev/null
+++ b/src/server/ApiManagers/FireflyManager.ts
@@ -0,0 +1,51 @@
+import { DashUploadUtils } from '../DashUploadUtils';
+import { _invalid, _success, Method } from '../RouteManager';
+import ApiManager, { Registration } from './ApiManager';
+
+export default class FireflyManager extends ApiManager {
+ askFirefly = (prompt: string = 'a realistic illustration of a cat coding') => {
+ const fetched = fetch('https://ims-na1.adobelogin.com/ims/token/v3', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ body: `grant_type=client_credentials&client_id=${process.env._CLIENT_FIREFLY_CLIENT_ID}&client_secret=${process.env._CLIENT_FIREFLY_SECRET}&scope=openid,AdobeID,session,additional_info,read_organizations,firefly_api,ff_apis`,
+ })
+ .then(response => response.json())
+ .then((data: { access_token: string }) =>
+ fetch('https://firefly-api.adobe.io/v3/images/generate', {
+ method: 'POST',
+ headers: [
+ ['Content-Type', 'application/json'],
+ ['Accept', 'application/json'],
+ ['x-api-key', process.env._CLIENT_FIREFLY_CLIENT_ID ?? ''],
+ ['Authorization', `Bearer ${data.access_token}`],
+ ],
+ body: `{ "prompt": "${prompt}" }`,
+ })
+ .then(response => response.json().then(json => JSON.stringify((json.outputs?.[0] as { image: { url: string } })?.image)))
+ .catch(error => {
+ console.error('Error:', error);
+ return '';
+ })
+ )
+ .catch(error => {
+ console.error('Error:', error);
+ return '';
+ });
+ return fetched;
+ };
+ protected initialize(register: Registration): void {
+ register({
+ method: Method.POST,
+ subscription: '/queryFireflyImage',
+ secureHandler: ({ req, res }) =>
+ this.askFirefly(req.body.prompt).then(fire =>
+ DashUploadUtils.UploadImage(JSON.parse(fire).url).then(info => {
+ if (info instanceof Error) _invalid(res, info.message);
+ else _success(res, info.accessPaths.agnostic.client);
+ })
+ ),
+ });
+ }
+}
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index 1e55a885a..032d13d43 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -369,7 +369,8 @@ export namespace DashUploadUtils {
*/
export const UploadInspectedImage = async (metadata: Upload.InspectionResults, filename: string, prefix = '', cleanUp = true): Promise<Upload.ImageInformation> => {
const { requestable, source, ...remaining } = metadata;
- const resolved = filename || `${prefix}upload_${Utils.GenerateGuid()}.${remaining.contentType.split('/')[1].toLowerCase()}`;
+ const dfltSuffix = remaining.contentType.split('/')[1].toLowerCase();
+ const resolved = filename || `${prefix}upload_${Utils.GenerateGuid()}.${dfltSuffix === 'xml' ? 'jpg' : dfltSuffix}`;
const { images } = Directory;
const information: Upload.ImageInformation = {
accessPaths: {
diff --git a/src/server/index.ts b/src/server/index.ts
index 88dbd232d..1f9af9ee0 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -7,6 +7,7 @@ import AssistantManager from './ApiManagers/AssistantManager';
import DataVizManager from './ApiManagers/DataVizManager';
import DeleteManager from './ApiManagers/DeleteManager';
import DownloadManager from './ApiManagers/DownloadManager';
+import FireflyManager from './ApiManagers/FireflyManager';
import GeneralGoogleManager from './ApiManagers/GeneralGoogleManager';
import SessionManager from './ApiManagers/SessionManager';
import UploadManager from './ApiManagers/UploadManager';
@@ -71,6 +72,7 @@ function routeSetter({ addSupervisedRoute, logRegistrationOutcome }: RouteManage
new GeneralGoogleManager(),
/* new GooglePhotosManager(), */ new DataVizManager(),
new AssistantManager(),
+ new FireflyManager(),
];
// initialize API Managers
@@ -112,7 +114,6 @@ function routeSetter({ addSupervisedRoute, logRegistrationOutcome }: RouteManage
});
const serve: PublicHandler = ({ req, res }) => {
- // eslint-disable-next-line new-cap
const detector = new mobileDetect(req.headers['user-agent'] || '');
const filename = detector.mobile() !== null ? 'mobile/image.html' : 'index.html';
res.sendFile(path.join(__dirname, '../../deploy/' + filename));
diff --git a/webpack.config.js b/webpack.config.js
index e1afc64e5..67417fb02 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,4 +1,4 @@
-/* eslint-disable @typescript-eslint/no-var-requires */
+/* eslint-disable @typescript-eslint/no-require-imports */
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
@@ -36,7 +36,6 @@ function transferEnvironmentVariables() {
}
const resolvedClientSide = Object.keys(parsed).reduce((mapping, envKey) => {
if (envKey.startsWith(prefix)) {
- // eslint-disable-next-line
mapping[`process.env.${envKey.replace(prefix, '')}`] = JSON.stringify(parsed[envKey]);
}
return mapping;
@@ -112,7 +111,7 @@ module.exports = {
test: /\.scss|css$/,
exclude: /\.module\.scss$/i,
use: [
- { loader: 'style-loader' }, // eslint-disable-next-line prettier/prettier
+ { loader: 'style-loader' }, //
{ loader: 'css-loader' },
{ loader: 'sass-loader' },
],
@@ -127,7 +126,7 @@ module.exports = {
{
test: /\.module\.scss$/i,
use: [
- { loader: 'style-loader' }, // eslint-disable-next-line prettier/prettier
+ { loader: 'style-loader' }, //
{ loader: 'css-loader', options: { modules: true } },
{ loader: 'sass-loader' },
],