From b6537cce6aa34eb33c052d7ec2cbbf804be08fba Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Wed, 28 Jun 2023 11:47:41 -0400 Subject: added files --- .../views/nodes/generativeFill/GenerativeFill.scss | 96 +++++++ .../views/nodes/generativeFill/GenerativeFill.tsx | 308 +++++++++++++++++++++ .../generativeFill/GenerativeFillButtons.scss | 4 + .../nodes/generativeFill/GenerativeFillButtons.tsx | 58 ++++ .../generativeFillUtils/BrushHandler.ts | 87 ++++++ .../GenerativeFillMathHelpers.ts | 11 + .../generativeFillUtils/ImageHandler.ts | 146 ++++++++++ .../generativeFillUtils/PointerHandler.ts | 15 + .../generativeFillUtils/generativeFillConstants.ts | 5 + .../generativeFillInterfaces.ts | 16 ++ 10 files changed, 746 insertions(+) create mode 100644 src/client/views/nodes/generativeFill/GenerativeFill.scss create mode 100644 src/client/views/nodes/generativeFill/GenerativeFill.tsx create mode 100644 src/client/views/nodes/generativeFill/GenerativeFillButtons.scss create mode 100644 src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx create mode 100644 src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts create mode 100644 src/client/views/nodes/generativeFill/generativeFillUtils/GenerativeFillMathHelpers.ts create mode 100644 src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts create mode 100644 src/client/views/nodes/generativeFill/generativeFillUtils/PointerHandler.ts create mode 100644 src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts create mode 100644 src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillInterfaces.ts (limited to 'src') diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.scss b/src/client/views/nodes/generativeFill/GenerativeFill.scss new file mode 100644 index 000000000..92406ba9d --- /dev/null +++ b/src/client/views/nodes/generativeFill/GenerativeFill.scss @@ -0,0 +1,96 @@ +$navHeight: 5rem; +$canvasSize: 1024px; +$scale: 0.5; + +.generativeFillContainer { + position: absolute; + top: 0; + left: 0; + z-index: 999; + height: 100vh; + width: 100vw; + display: flex; + flex-direction: column; + overflow: hidden; + + .controls { + flex-shrink: 0; + height: $navHeight; + background-color: #ffffff; + z-index: 999; + width: 100%; + display: flex; + gap: 3rem; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid #c7cdd0; + padding: 0 2rem; + + h1 { + font-size: 1.5rem; + } + } + + .drawingArea { + cursor: none; + touch-action: none; + position: relative; + flex-grow: 1; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + background-color: #f0f4f6; + + canvas { + display: block; + position: absolute; + transform-origin: 50% 50%; + } + + .pointer { + pointer-events: none; + position: absolute; + border-radius: 50%; + width: 50px; + height: 50px; + border: 1px solid #ffffff; + transform: translate(-50%, -50%); + display: flex; + justify-content: center; + align-items: center; + + .innerPointer { + width: 100%; + height: 100%; + border: 1px solid #000000; + border-radius: 50%; + } + } + + .iconContainer { + position: absolute; + top: 2rem; + left: 2rem; + display: flex; + flex-direction: column; + gap: 2rem; + } + + .editsBox { + position: absolute; + top: 2rem; + right: 2rem; + display: flex; + flex-direction: column; + gap: 1rem; + + img { + transition: all 0.2s ease-in-out; + &:hover { + opacity: 0.8; + } + } + } + } +} diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx new file mode 100644 index 000000000..6dd80a5d1 --- /dev/null +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -0,0 +1,308 @@ +import { useEffect, useRef, useState } from 'react'; +import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler'; +import { BrushHandler } from './generativeFillUtils/BrushHandler'; +import { IconButton, TextField } from '@mui/material'; +import { CursorData, Point } from './generativeFillUtils/generativeFillInterfaces'; +import { activeColor, canvasSize, eraserColor } from './generativeFillUtils/generativeFillConstants'; +import { PointerHandler } from './generativeFillUtils/PointerHandler'; +import { BsBrush, BsEraser } from 'react-icons/bs'; +import { AiOutlineUpload } from 'react-icons/ai'; +import { CiUndo, CiRedo } from 'react-icons/ci'; +import Buttons from './GenerativeFillButtons'; +import React from 'react'; +import './GenerativeFill.scss'; + +/** + * For images not 1024x1024 fill in the rest in solid black, or a + * reflected version of the image. + */ + +/** + * TODO: Look into img onload, sometimes the canvas doesn't update properly + */ + +enum BrushStyle { + ADD, + SUBTRACT, + MARQUEE, +} + +interface ImageEdit { + imgElement: HTMLImageElement; + parent: ImageEdit; + children: ImageEdit[]; +} + +const GenerativeFill = () => { + const canvasRef = useRef(null); + const canvasBackgroundRef = useRef(null); + const drawingAreaRef = useRef(null); + const fileRef = useRef(null); + const [cursorData, setCursorData] = useState({ + x: 0, + y: 0, + width: 150, + }); + const [isBrushing, setIsBrushing] = useState(false); + const [canvasScale, setCanvasScale] = useState(0.5); + const [edits, setEdits] = useState([]); + const [brushStyle, setBrushStyle] = useState(BrushStyle.ADD); + const [input, setInput] = useState(''); + const [loading, setLoading] = useState(false); + // used to store the current image loaded to the main canvas + const currImg = useRef(null); + // ref to store history + const undoStack = useRef([]); + + // initiate brushing + const handlePointerDown = (e: React.PointerEvent) => { + const canvas = canvasRef.current; + if (!canvas) return; + const ctx = ImageUtility.getCanvasContext(canvasRef); + if (!ctx) return; + + setIsBrushing(true); + const { x, y } = PointerHandler.getPointRelativeToElement(canvas, e, canvasScale); + + BrushHandler.brushCircleOverlay(x, y, cursorData.width / 2 / canvasScale, ctx, eraserColor, brushStyle === BrushStyle.SUBTRACT); + }; + + // stop brushing, push to undo stack + const handlePointerUp = (e: React.PointerEvent) => { + const ctx = ImageUtility.getCanvasContext(canvasBackgroundRef); + if (!ctx) return; + if (!isBrushing) return; + setIsBrushing(false); + }; + + // handles brushing on pointer movement + useEffect(() => { + if (!isBrushing) return; + const canvas = canvasRef.current; + if (!canvas) return; + const ctx = ImageUtility.getCanvasContext(canvasRef); + if (!ctx) return; + + const handlePointerMove = (e: PointerEvent) => { + const currPoint = PointerHandler.getPointRelativeToElement(canvas, e, canvasScale); + const lastPoint: Point = { + x: currPoint.x - e.movementX / canvasScale, + y: currPoint.y - e.movementY / canvasScale, + }; + BrushHandler.createBrushPathOverlay(lastPoint, currPoint, cursorData.width / 2 / canvasScale, ctx, eraserColor, brushStyle === BrushStyle.SUBTRACT); + }; + + drawingAreaRef.current?.addEventListener('pointermove', handlePointerMove); + return () => drawingAreaRef.current?.removeEventListener('pointermove', handlePointerMove); + }, [isBrushing]); + + // first load + useEffect(() => { + const img = new Image(); + img.src = '/assets/art.jpeg'; + ImageUtility.drawImgToCanvas(img, canvasRef); + currImg.current = img; + }, [canvasRef]); + + useEffect(() => { + if (!canvasBackgroundRef.current) return; + const ctx = ImageUtility.getCanvasContext(canvasBackgroundRef); + if (!ctx) return; + }, [canvasBackgroundRef]); + + // handles brush sizing + useEffect(() => { + const handleKeyPress = (e: KeyboardEvent) => { + if (e.key === 'ArrowUp') { + e.preventDefault(); + e.stopPropagation(); + setCursorData(data => ({ ...data, width: data.width + 5 })); + } else if (e.key === 'ArrowDown') { + e.preventDefault(); + e.stopPropagation(); + setCursorData(data => (data.width >= 20 ? { ...data, width: data.width - 5 } : data)); + } + }; + window.addEventListener('keydown', handleKeyPress); + return () => window.removeEventListener('keydown', handleKeyPress); + }, []); + + // handle pinch zoom + useEffect(() => { + const handlePinch = (e: WheelEvent) => { + e.preventDefault(); + e.stopPropagation(); + const delta = e.deltaY; + const scaleFactor = delta > 0 ? 0.98 : 1.02; // Adjust the scale factor as per your requirement + setCanvasScale(prevScale => prevScale * scaleFactor); + }; + + drawingAreaRef.current?.addEventListener('wheel', handlePinch, { + passive: false, + }); + return () => drawingAreaRef.current?.removeEventListener('wheel', handlePinch); + }, [drawingAreaRef]); + + // updates the current position of the cursor + const updateCursorData = (e: React.PointerEvent) => { + const drawingArea = drawingAreaRef.current; + if (!drawingArea) return; + const { x, y } = PointerHandler.getPointRelativeToElement(drawingArea, e, 1); + setCursorData(data => ({ + ...data, + x, + y, + })); + }; + + // File upload + const uploadImg = (e: React.ChangeEvent) => { + if (e.target.files) { + const file = e.target.files[0]; + const image = new Image(); + const imgUrl = URL.createObjectURL(file); + image.src = imgUrl; + ImageUtility.drawImgToCanvas(image, canvasRef); + currImg.current = image; + } + }; + + // Get AI Edit + const getEdit = async () => { + const img = currImg.current; + if (!img) return; + const canvas = canvasRef.current; + if (!canvas) return; + const ctx = ImageUtility.getCanvasContext(canvasRef); + if (!ctx) return; + setLoading(true); + try { + const maskBlob = await ImageUtility.canvasToBlob(canvas); + const imgBlob = await ImageUtility.canvasToBlob(ImageUtility.getCanvasImg(img)); + + // const res = await ImageUtility.getEdit( + // imgBlob, + // maskBlob, + // input !== "" + // ? input + " in the same style" + // : "Fill in the image in the same style", + // 1 + // ); + + const res = await ImageUtility.mockGetEdit(); + const { urls } = res as APISuccess; + const image = new Image(); + image.src = urls[0]; + setLoading(false); + setEdits(urls); + ImageUtility.drawImgToCanvas(image, canvasRef); + currImg.current = image; + } catch (err) { + console.log(err); + } + }; + + return ( +
+
+

Generative Fill

+ +
+ {/* Main canvas for editing */} +
+ + +
+
+
+ {/* Icons */} +
+ + { + if (fileRef.current) { + fileRef.current.click(); + } + }}> + + + { + setBrushStyle(BrushStyle.ADD); + }}> + + + { + setBrushStyle(BrushStyle.SUBTRACT); + }}> + + + {/* Undo and Redo */} + {/* { + e.stopPropagation(); + console.log(undoStack.current); + }} + onPointerUp={e => { + e.stopPropagation(); + }}> + + + {}}> + + */} +
+ {/* Edits box */} +
+ {edits.map(edit => ( + { + const img = new Image(); + img.src = edit; + ImageUtility.drawImgToCanvas(img, canvasRef); + currImg.current = img; + }} + /> + ))} +
+
+
+ setInput(e.target.value)} + disabled={isBrushing} + type="text" + label="Prompt" + placeholder="Prompt..." + sx={{ + backgroundColor: '#ffffff', + position: 'absolute', + bottom: '1rem', + transform: 'translateX(calc(50vw - 50%))', + width: 'calc(100vw - 4rem)', + }} + /> +
+
+ ); +}; + +export default GenerativeFill; diff --git a/src/client/views/nodes/generativeFill/GenerativeFillButtons.scss b/src/client/views/nodes/generativeFill/GenerativeFillButtons.scss new file mode 100644 index 000000000..0180ef904 --- /dev/null +++ b/src/client/views/nodes/generativeFill/GenerativeFillButtons.scss @@ -0,0 +1,4 @@ +.generativeFillBtnContainer { + display: flex; + gap: 1rem; +} diff --git a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx new file mode 100644 index 000000000..348e27a16 --- /dev/null +++ b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx @@ -0,0 +1,58 @@ +import { Button } from '@mui/material'; +import { ImageUtility } from './generativeFillUtils/ImageHandler'; +import { canvasSize } from './generativeFillUtils/generativeFillConstants'; +import { Oval } from 'react-loader-spinner'; +import './GenerativeFillButtons.scss'; +import React from 'react'; + +interface ButtonContainerProps { + canvasRef: React.RefObject; + backgroundref: React.RefObject; + currImg: React.MutableRefObject; + undoStack: React.MutableRefObject; + getEdit: () => Promise; + loading: boolean; +} + +const Buttons = ({ canvasRef, backgroundref, currImg, undoStack, loading, getEdit }: ButtonContainerProps) => { + const handleReset = () => { + if (!canvasRef.current || !currImg.current) return; + const ctx = ImageUtility.getCanvasContext(canvasRef); + if (!ctx) return; + ctx.clearRect(0, 0, canvasSize, canvasSize); + ImageUtility.drawImgToCanvas(currImg.current, canvasRef, true); + }; + + return ( +
+ + + {/* */} + +
+ ); +}; + +export default Buttons; diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts new file mode 100644 index 000000000..c2716e083 --- /dev/null +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts @@ -0,0 +1,87 @@ +import { GenerativeFillMathHelpers } from "./GenerativeFillMathHelpers"; +import { eraserColor } from "./generativeFillConstants"; +import { Point } from "./generativeFillInterfaces"; + +export class BrushHandler { + static brushCircle = ( + x: number, + y: number, + brushRadius: number, + ctx: CanvasRenderingContext2D + ) => { + ctx.globalCompositeOperation = "destination-out"; + ctx.shadowColor = "#ffffffeb"; + ctx.shadowBlur = 5; + ctx.beginPath(); + ctx.arc(x, y, brushRadius, 0, 2 * Math.PI); + ctx.fill(); + ctx.closePath(); + }; + + static brushCircleOverlay = ( + x: number, + y: number, + brushRadius: number, + ctx: CanvasRenderingContext2D, + fillColor: string, + erase: boolean + ) => { + ctx.globalCompositeOperation = "destination-out"; + // ctx.globalCompositeOperation = erase ? "destination-out" : "source-over"; + ctx.fillStyle = fillColor; + ctx.shadowColor = eraserColor; + ctx.shadowBlur = 5; + ctx.beginPath(); + ctx.arc(x, y, brushRadius, 0, 2 * Math.PI); + ctx.fill(); + ctx.closePath(); + }; + + static createBrushPath = ( + startPoint: Point, + endPoint: Point, + brushRadius: number, + ctx: CanvasRenderingContext2D + ) => { + const dist = GenerativeFillMathHelpers.distanceBetween( + startPoint, + endPoint + ); + + for (let i = 0; i < dist; i += 5) { + const s = i / dist; + BrushHandler.brushCircle( + startPoint.x * (1 - s) + endPoint.x * s, + startPoint.y * (1 - s) + endPoint.y * s, + brushRadius, + ctx + ); + } + }; + + static createBrushPathOverlay = ( + startPoint: Point, + endPoint: Point, + brushRadius: number, + ctx: CanvasRenderingContext2D, + fillColor: string, + erase: boolean + ) => { + const dist = GenerativeFillMathHelpers.distanceBetween( + startPoint, + endPoint + ); + + for (let i = 0; i < dist; i += 5) { + const s = i / dist; + BrushHandler.brushCircleOverlay( + startPoint.x * (1 - s) + endPoint.x * s, + startPoint.y * (1 - s) + endPoint.y * s, + brushRadius, + ctx, + fillColor, + erase + ); + } + }; +} diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/GenerativeFillMathHelpers.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/GenerativeFillMathHelpers.ts new file mode 100644 index 000000000..027b99a52 --- /dev/null +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/GenerativeFillMathHelpers.ts @@ -0,0 +1,11 @@ +import { Point } from "./generativeFillInterfaces"; + +export class GenerativeFillMathHelpers { + // math helpers + static distanceBetween = (p1: Point, p2: Point) => { + return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2)); + }; + static angleBetween = (p1: Point, p2: Point) => { + return Math.atan2(p2.x - p1.x, p2.y - p1.y); + }; +} diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts new file mode 100644 index 000000000..1c726afbb --- /dev/null +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts @@ -0,0 +1,146 @@ +import { RefObject } from "react"; +import { OPENAI_KEY } from "../keys"; +import { canvasSize } from "./generativeFillConstants"; + +export interface APISuccess { + status: "success"; + urls: string[]; +} + +export interface APIError { + status: "error"; + message: string; +} + +export class ImageUtility { + static canvasToBlob = (canvas: HTMLCanvasElement): Promise => { + return new Promise((resolve) => { + canvas.toBlob((blob) => { + if (blob) { + resolve(blob); + } + }, "image/png"); + }); + }; + + static getEdit = async ( + imgBlob: Blob, + maskBlob: Blob, + prompt: string, + n?: number + ): Promise => { + const apiUrl = "https://api.openai.com/v1/images/edits"; + const fd = new FormData(); + fd.append("image", imgBlob, "image.png"); + fd.append("mask", maskBlob, "mask.png"); + fd.append("prompt", prompt); + fd.append("size", "1024x1024"); + fd.append("n", n ? JSON.stringify(n) : "1"); + fd.append("response_format", "b64_json"); + + try { + const res = await fetch(apiUrl, { + method: "POST", + headers: { + Authorization: `Bearer ${OPENAI_KEY}`, + }, + body: fd, + }); + const data = await res.json(); + console.log(data.data); + return { + status: "success", + urls: (data.data as { b64_json: string }[]).map( + (data) => `data:image/png;base64,${data.b64_json}` + ), + }; + } catch (err) { + console.log(err); + return { status: "error", message: "API error." }; + } + }; + + static mockGetEdit = async (): Promise => { + return { + status: "success", + urls: [ + "/assets/shiba.png", + "/assets/souffle-dalle.png", + "/assets/firefly.png", + ], + }; + }; + + static getCanvasContext = ( + canvasRef: RefObject + ): CanvasRenderingContext2D | null => { + if (!canvasRef.current) return null; + const ctx = canvasRef.current.getContext("2d"); + if (!ctx) return null; + return ctx; + }; + + static downloadCanvas = (canvas: HTMLCanvasElement) => { + const url = canvas.toDataURL(); + const downloadLink = document.createElement("a"); + downloadLink.href = url; + downloadLink.download = "canvas"; + + downloadLink.click(); + downloadLink.remove(); + }; + + static downloadImageCanvas = (imgUrl: string) => { + const img = new Image(); + img.src = imgUrl; + img.onload = () => { + const canvas = document.createElement("canvas"); + canvas.width = canvasSize; + canvas.height = canvasSize; + const ctx = canvas.getContext("2d"); + ctx?.drawImage(img, 0, 0, canvasSize, canvasSize); + + this.downloadCanvas(canvas); + }; + }; + + static drawImgToCanvas = ( + img: HTMLImageElement, + canvasRef: React.RefObject, + loaded?: boolean + ) => { + if (loaded) { + const ctx = this.getCanvasContext(canvasRef); + if (!ctx) return; + ctx.globalCompositeOperation = "source-over"; + const scale = Math.min(canvasSize / img.width, canvasSize / img.height); + const width = img.width * scale; + const height = img.height * scale; + ctx.clearRect(0, 0, canvasSize, canvasSize); + ctx.drawImage(img, 0, 0, width, height); + } else { + img.onload = () => { + const ctx = this.getCanvasContext(canvasRef); + if (!ctx) return; + ctx.globalCompositeOperation = "source-over"; + const scale = Math.min(canvasSize / img.width, canvasSize / img.height); + const width = img.width * scale; + const height = img.height * scale; + ctx.clearRect(0, 0, canvasSize, canvasSize); + ctx.drawImage(img, 0, 0, width, height); + }; + } + }; + + // The image must be loaded! + static getCanvasImg = (img: HTMLImageElement): HTMLCanvasElement => { + const canvas = document.createElement("canvas"); + canvas.width = canvasSize; + canvas.height = canvasSize; + const ctx = canvas.getContext("2d"); + ctx?.clearRect(0, 0, canvasSize, canvasSize); + ctx?.drawImage(img, 0, 0, canvasSize, canvasSize); + + return canvas; + }; +} diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/PointerHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/PointerHandler.ts new file mode 100644 index 000000000..9e620ad11 --- /dev/null +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/PointerHandler.ts @@ -0,0 +1,15 @@ +import { Point } from "./generativeFillInterfaces"; + +export class PointerHandler { + static getPointRelativeToElement = ( + element: HTMLElement, + e: React.PointerEvent | PointerEvent, + scale: number + ): Point => { + const boundingBox = element.getBoundingClientRect(); + return { + x: (e.clientX - boundingBox.x) / scale, + y: (e.clientY - boundingBox.y) / scale, + }; + }; +} diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts new file mode 100644 index 000000000..78903b9aa --- /dev/null +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts @@ -0,0 +1,5 @@ +// constants +export const canvasSize = 1024; + +export const activeColor = "#1976d2"; +export const eraserColor = "#e1e9ec"; diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillInterfaces.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillInterfaces.ts new file mode 100644 index 000000000..9b9b9d3c2 --- /dev/null +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillInterfaces.ts @@ -0,0 +1,16 @@ +// interfaces +export interface CursorData { + x: number; + y: number; + width: number; +} + +export interface Point { + x: number; + y: number; +} + +export enum BrushMode { + ADD, + SUBTRACT, +} -- cgit v1.2.3-70-g09d2 From 932ecd6092bd1b0ac3391309a550bac76cfb0e04 Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Thu, 29 Jun 2023 13:56:41 -0400 Subject: undo and redo --- src/client/apis/gpt/GPT.ts | 2 +- src/client/views/MainView.tsx | 11 + src/client/views/nodes/ImageBox.tsx | 11 + .../views/nodes/formattedText/FormattedTextBox.tsx | 1 + .../views/nodes/generativeFill/GenerativeFill.scss | 4 +- .../views/nodes/generativeFill/GenerativeFill.tsx | 157 +++++++++++--- .../nodes/generativeFill/GenerativeFillButtons.tsx | 36 ++-- .../generativeFillUtils/ImageHandler.ts | 230 ++++++++++----------- .../generativeFillUtils/generativeFillConstants.ts | 5 +- 9 files changed, 273 insertions(+), 184 deletions(-) (limited to 'src') diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 4b3960902..18222b32a 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -39,7 +39,7 @@ const gptAPICall = async (inputText: string, callType: GPTCallType) => { temperature: opts.temp, prompt: `${opts.prompt}${inputText}`, }); - console.log(response.data.choices[0]); + // console.log(response.data.choices[0]); return response.data.choices[0].text; } catch (err) { console.log(err); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index c0c473cfc..1fa1eff84 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -65,6 +65,7 @@ import { PreviewCursor } from './PreviewCursor'; import { PropertiesView } from './PropertiesView'; import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider'; import { TopBar } from './topbar/TopBar'; +import GenerativeFill from './nodes/generativeFill/GenerativeFill'; const _global = (window /* browser */ || global) /* node */ as any; @observer @@ -72,6 +73,15 @@ export class MainView extends React.Component { public static Instance: MainView; public static Live: boolean = false; private _docBtnRef = React.createRef(); + // for ai image editor + @observable public imageEditorOpen: boolean = false; + @action public setImageEditorOpen = (open: boolean) => (this.imageEditorOpen = open); + @observable public imageEditorSource: string = ''; + @action public setImageEditorSource = (source: string) => (this.imageEditorSource = source); + @observable public imageRootDoc: Doc | undefined; + @action public setImageRootDoc = (doc: Doc) => (this.imageRootDoc = doc); + @observable public addDoc: ((doc: Doc | Doc[], annotationKey?: string) => boolean) | undefined; + @observable public LastButton: Opt; @observable private _windowWidth: number = 0; @observable private _windowHeight: number = 0; @@ -1014,6 +1024,7 @@ export class MainView extends React.Component { {this.snapLines} + ); } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 5b302e7ce..2c6f92681 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -38,6 +38,7 @@ import './ImageBox.scss'; import { PinProps, PresBox } from './trails'; import React = require('react'); import Color = require('color'); +import { MainView } from '../MainView'; export const pageSchema = createSchema({ googlePhotosUrl: 'string', @@ -248,6 +249,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent Utils.CopyText(this.choosePath(field.url)), icon: 'copy' }); + funcs.push({ + description: 'Open Image Editor', + event: action(() => { + MainView.Instance.setImageEditorOpen(true); + MainView.Instance.setImageEditorSource(this.choosePath(field.url)); + MainView.Instance.addDoc = this.props.addDocument; + MainView.Instance.imageRootDoc = this.rootDoc; + }), + icon: 'pencil-alt', + }); if (!Doc.noviceMode) { funcs.push({ description: 'Export to Google Photos', event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: 'caret-square-right' }); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 0dc186eaf..3fa5c099b 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -882,6 +882,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { + console.log(this.gptRes); if (resIndex < this.gptRes.length) { this.dataDoc.text = (this.dataDoc.text as RichTextField)?.Text + this.gptRes[resIndex]; setTimeout(() => { diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.scss b/src/client/views/nodes/generativeFill/GenerativeFill.scss index 92406ba9d..b1e570cf1 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.scss +++ b/src/client/views/nodes/generativeFill/GenerativeFill.scss @@ -6,14 +6,14 @@ $scale: 0.5; position: absolute; top: 0; left: 0; - z-index: 999; + z-index: 9999; height: 100vh; width: 100vw; display: flex; flex-direction: column; overflow: hidden; - .controls { + .generativeFillControls { flex-shrink: 0; height: $navHeight; background-color: #ffffff; diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index 6dd80a5d1..0df05f4d7 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -3,14 +3,21 @@ import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler'; import { BrushHandler } from './generativeFillUtils/BrushHandler'; import { IconButton, TextField } from '@mui/material'; import { CursorData, Point } from './generativeFillUtils/generativeFillInterfaces'; -import { activeColor, canvasSize, eraserColor } from './generativeFillUtils/generativeFillConstants'; +import { activeColor, canvasSize, eraserColor, freeformRenderSize } from './generativeFillUtils/generativeFillConstants'; import { PointerHandler } from './generativeFillUtils/PointerHandler'; -import { BsBrush, BsEraser } from 'react-icons/bs'; +import { BsBrush, BsEraser, BsX } from 'react-icons/bs'; import { AiOutlineUpload } from 'react-icons/ai'; import { CiUndo, CiRedo } from 'react-icons/ci'; import Buttons from './GenerativeFillButtons'; -import React from 'react'; +import React = require('react'); import './GenerativeFill.scss'; +import { EditableText } from 'browndash-components'; +import { MainView } from '../../MainView'; +import { Doc } from '../../../../fields/Doc'; +import { Networking } from '../../../Network'; +import { Utils } from '../../../../Utils'; +import { DocUtils, Docs } from '../../../documents/Documents'; +import { NumCast } from '../../../../fields/Types'; /** * For images not 1024x1024 fill in the rest in solid black, or a @@ -33,7 +40,14 @@ interface ImageEdit { children: ImageEdit[]; } -const GenerativeFill = () => { +interface GenerativeFillProps { + imageEditorOpen: boolean; + imageEditorSource: string; + imageRootDoc: Doc | undefined; + addDoc: ((doc: Doc | Doc[], annotationKey?: string) => boolean) | undefined; +} + +const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc }: GenerativeFillProps) => { const canvasRef = useRef(null); const canvasBackgroundRef = useRef(null); const drawingAreaRef = useRef(null); @@ -51,8 +65,49 @@ const GenerativeFill = () => { const [loading, setLoading] = useState(false); // used to store the current image loaded to the main canvas const currImg = useRef(null); - // ref to store history - const undoStack = useRef([]); + const originalImg = useRef(null); + // stores history of data urls + const undoStack = useRef([]); + // stores redo stack + const redoStack = useRef([]); + + // Undo and Redo + const handleUndo = () => { + const ctx = ImageUtility.getCanvasContext(canvasRef); + if (!ctx || !currImg.current || !canvasRef.current) return; + + const target = undoStack.current[undoStack.current.length - 1]; + if (!target) { + ImageUtility.drawImgToCanvas(currImg.current, canvasRef); + } else { + redoStack.current = [...redoStack.current, canvasRef.current.toDataURL()]; + const img = new Image(); + img.src = target; + ImageUtility.drawImgToCanvas(img, canvasRef); + undoStack.current = undoStack.current.slice(0, -1); + } + }; + + const handleRedo = () => { + // TODO: handle undo as well + const target = redoStack.current[redoStack.current.length - 1]; + if (!target) { + } else { + const img = new Image(); + img.src = target; + ImageUtility.drawImgToCanvas(img, canvasRef); + redoStack.current = redoStack.current.slice(0, -1); + } + }; + + const handleReset = () => { + if (!canvasRef.current || !currImg.current) return; + const ctx = ImageUtility.getCanvasContext(canvasRef); + if (!ctx) return; + ctx.clearRect(0, 0, canvasSize, canvasSize); + undoStack.current = []; + ImageUtility.drawImgToCanvas(currImg.current, canvasRef, true); + }; // initiate brushing const handlePointerDown = (e: React.PointerEvent) => { @@ -61,6 +116,9 @@ const GenerativeFill = () => { const ctx = ImageUtility.getCanvasContext(canvasRef); if (!ctx) return; + undoStack.current = [...undoStack.current, canvasRef.current.toDataURL()]; + redoStack.current = []; + setIsBrushing(true); const { x, y } = PointerHandler.getPointRelativeToElement(canvas, e, canvasScale); @@ -98,17 +156,13 @@ const GenerativeFill = () => { // first load useEffect(() => { + if (!imageEditorSource || imageEditorSource === '') return; const img = new Image(); - img.src = '/assets/art.jpeg'; + img.src = imageEditorSource; ImageUtility.drawImgToCanvas(img, canvasRef); currImg.current = img; - }, [canvasRef]); - - useEffect(() => { - if (!canvasBackgroundRef.current) return; - const ctx = ImageUtility.getCanvasContext(canvasBackgroundRef); - if (!ctx) return; - }, [canvasBackgroundRef]); + originalImg.current = img; + }, [canvasRef, imageEditorSource]); // handles brush sizing useEffect(() => { @@ -180,16 +234,9 @@ const GenerativeFill = () => { const maskBlob = await ImageUtility.canvasToBlob(canvas); const imgBlob = await ImageUtility.canvasToBlob(ImageUtility.getCanvasImg(img)); - // const res = await ImageUtility.getEdit( - // imgBlob, - // maskBlob, - // input !== "" - // ? input + " in the same style" - // : "Fill in the image in the same style", - // 1 - // ); + const res = await ImageUtility.getEdit(imgBlob, maskBlob, input !== '' ? input + ' in the same style' : 'Fill in the image in the same style', 2); - const res = await ImageUtility.mockGetEdit(); + // const res = await ImageUtility.mockGetEdit(); const { urls } = res as APISuccess; const image = new Image(); image.src = urls[0]; @@ -202,11 +249,47 @@ const GenerativeFill = () => { } }; + const onSave = async () => { + if (!currImg.current || !imageRootDoc) return; + try { + const src = currImg.current.src; + console.log(src); + const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [src] }); + const source = Utils.prepend(result.accessPaths.agnostic.client); + console.log(source); + const newImg = Docs.Create.ImageDocument(source, { + x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + 20, + y: NumCast(imageRootDoc.y), + _height: freeformRenderSize, + _width: freeformRenderSize, + data_nativeWidth: result.nativeWidth, + data_nativeHeight: result.nativeHeight, + }); + + addDoc?.(newImg); + // Create link between prompt and image + DocUtils.MakeLink(imageRootDoc, newImg, { link_relationship: 'Image Edit' }); + console.log('done'); + } catch (err) { + console.log(err); + } + }; + return ( -
-
+
+

Generative Fill

- +
+ + { + MainView.Instance.setImageEditorOpen(false); + MainView.Instance.setImageEditorSource(''); + setEdits([]); + }}> + + +
{/* Main canvas for editing */}
{ }}> - { setBrushStyle(BrushStyle.SUBTRACT); }}> - + */} {/* Undo and Redo */} - {/* { e.stopPropagation(); - console.log(undoStack.current); + handleUndo(); }} onPointerUp={e => { e.stopPropagation(); }}> - {}}> + { + e.stopPropagation(); + handleRedo(); + }} + onPointerUp={e => { + e.stopPropagation(); + }}> - */} +
{/* Edits box */}
@@ -292,12 +382,15 @@ const GenerativeFill = () => { type="text" label="Prompt" placeholder="Prompt..." + InputLabelProps={{ style: { fontSize: '1.5rem' } }} + inputProps={{ style: { fontSize: '1.5rem' } }} sx={{ backgroundColor: '#ffffff', position: 'absolute', bottom: '1rem', transform: 'translateX(calc(50vw - 50%))', width: 'calc(100vw - 4rem)', + scale: 1.2, }} />
diff --git a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx index 348e27a16..e8cb61ab5 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx @@ -3,44 +3,34 @@ import { ImageUtility } from './generativeFillUtils/ImageHandler'; import { canvasSize } from './generativeFillUtils/generativeFillConstants'; import { Oval } from 'react-loader-spinner'; import './GenerativeFillButtons.scss'; -import React from 'react'; +import React = require('react'); +import { Doc } from '../../../../fields/Doc'; interface ButtonContainerProps { canvasRef: React.RefObject; - backgroundref: React.RefObject; currImg: React.MutableRefObject; - undoStack: React.MutableRefObject; getEdit: () => Promise; loading: boolean; + onSave: () => Promise; + onReset: () => void; } -const Buttons = ({ canvasRef, backgroundref, currImg, undoStack, loading, getEdit }: ButtonContainerProps) => { - const handleReset = () => { - if (!canvasRef.current || !currImg.current) return; - const ctx = ImageUtility.getCanvasContext(canvasRef); - if (!ctx) return; - ctx.clearRect(0, 0, canvasSize, canvasSize); - ImageUtility.drawImgToCanvas(currImg.current, canvasRef, true); +const Buttons = ({ canvasRef, currImg, loading, getEdit, onSave, onReset }: ButtonContainerProps) => { + const handleSave = () => { + onSave(); }; return (
- - + + {/* - {/* */} + Download Original + */}
diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts index 91f12f866..48055903c 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts @@ -52,10 +52,10 @@ export class ImageUtility { } }; - static mockGetEdit = async (): Promise => { + static mockGetEdit = async (mockSrc: string): Promise => { return { status: 'success', - urls: ['/assets/shiba.png', '/assets/souffle-dalle.png', '/assets/firefly.png'], + urls: [mockSrc, mockSrc, mockSrc], }; }; -- cgit v1.2.3-70-g09d2 From f8d54bcc8cdb8d302be4ce3d5172ab59a1f574e7 Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Thu, 6 Jul 2023 02:44:19 -0400 Subject: just saving progress --- .../CollectionFreeFormLinkView.tsx | 11 ++- .../views/nodes/generativeFill/GenerativeFill.tsx | 108 +++++++++++++++------ .../generativeFillUtils/generativeFillConstants.ts | 4 +- 3 files changed, 91 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index f1d98d22a..bdc0e1599 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -21,6 +21,8 @@ export interface CollectionFreeFormLinkViewProps { LinkDocs: Doc[]; } +// props.screentolocatransform + @observer export class CollectionFreeFormLinkView extends React.Component { @observable _opacity: number = 0; @@ -235,8 +237,12 @@ export class CollectionFreeFormLinkView extends React.Component diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index ab7e3dfef..5e7b4fdca 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -1,23 +1,25 @@ +import './GenerativeFill.scss'; +import React = require('react'); import { useEffect, useRef, useState } from 'react'; import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler'; import { BrushHandler } from './generativeFillUtils/BrushHandler'; import { IconButton, TextField } from '@mui/material'; import { CursorData, Point } from './generativeFillUtils/generativeFillInterfaces'; -import { activeColor, canvasSize, eraserColor, freeformRenderSize } from './generativeFillUtils/generativeFillConstants'; +import { activeColor, canvasSize, eraserColor, freeformRenderSize, offsetDistanceY, offsetX } from './generativeFillUtils/generativeFillConstants'; import { PointerHandler } from './generativeFillUtils/PointerHandler'; import { BsBrush, BsEraser, BsX } from 'react-icons/bs'; import { AiOutlineUpload } from 'react-icons/ai'; import { CiUndo, CiRedo } from 'react-icons/ci'; import Buttons from './GenerativeFillButtons'; -import React = require('react'); -import './GenerativeFill.scss'; import { EditableText } from 'browndash-components'; import { MainView } from '../../MainView'; import { Doc } from '../../../../fields/Doc'; import { Networking } from '../../../Network'; import { Utils } from '../../../../Utils'; import { DocUtils, Docs } from '../../../documents/Documents'; -import { NumCast } from '../../../../fields/Types'; +import { DocCast, NumCast } from '../../../../fields/Types'; +import { CollectionDockingView } from '../../collections/CollectionDockingView'; +import { OpenWhere, OpenWhereMod } from '../DocumentView'; /** * For images not 1024x1024 fill in the rest in solid black, or a @@ -63,16 +65,23 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD const [brushStyle, setBrushStyle] = useState(BrushStyle.ADD); const [input, setInput] = useState(''); const [loading, setLoading] = useState(false); - // used to store the current image loaded to the main canvas + // the current image in the main canvas const currImg = useRef(null); + // the unedited version of each generation (parent) const originalImg = useRef(null); // stores history of data urls const undoStack = useRef([]); // stores redo stack const redoStack = useRef([]); - // will change later, for now, stores an array [, ] + + // early stage properly, likely will get rid of const freeformPosition = useRef([0, 0]); + // references to keep track of tree structure + const newCollectionRef = useRef(null); + const parentDoc = useRef(null); + const childrenDocs = useRef([]); + // Undo and Redo const handleUndo = () => { const ctx = ImageUtility.getCanvasContext(canvasRef); @@ -165,6 +174,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD ImageUtility.drawImgToCanvas(img, canvasRef); currImg.current = img; originalImg.current = img; + freeformPosition.current = [0, 0]; }, [canvasRef, imageEditorSource]); // handles brush sizing @@ -237,9 +247,8 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD const maskBlob = await ImageUtility.canvasToBlob(canvas); const imgBlob = await ImageUtility.canvasToBlob(ImageUtility.getCanvasImg(img)); - // const res = await ImageUtility.getEdit(imgBlob, maskBlob, input !== '' ? input + ' in the same style' : 'Fill in the image in the same style', 2); - - const res = await ImageUtility.mockGetEdit(img.src); + const res = await ImageUtility.getEdit(imgBlob, maskBlob, input !== '' ? input + ' in the same style' : 'Fill in the image in the same style', 2); + // const res = await ImageUtility.mockGetEdit(img.src); const { urls } = res as APISuccess; const image = new Image(); image.src = urls[0]; @@ -247,32 +256,73 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD setEdits(urls); ImageUtility.drawImgToCanvas(image, canvasRef); currImg.current = image; + freeformPosition.current[0] += 1; + freeformPosition.current[1] = 0; } catch (err) { console.log(err); } }; + // adjusts all the img positions to be aligned + const adjustImgPositions = () => { + if (!parentDoc.current) return; + const startY = NumCast(parentDoc.current.y); + const len = childrenDocs.current.length; + let initialYPositions: number[] = []; + for (let i = 0; i < len; i++) { + initialYPositions.push(startY + i * offsetDistanceY); + } + childrenDocs.current.forEach((doc, i) => { + if (len % 2 === 1) { + doc.y = initialYPositions[i] - Math.floor(len / 2) * offsetDistanceY; + } else { + doc.y = initialYPositions[i] - (len / 2 - 1 / 2) * offsetDistanceY; + } + }); + }; + + // creates a new image document and returns its reference + const createNewImgDoc = async (img: HTMLImageElement, firstDoc: boolean): Promise => { + if (!newCollectionRef.current || !imageRootDoc || !parentDoc.current) return; + const src = img.src; + const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [src] }); + const source = Utils.prepend(result.accessPaths.agnostic.client); + // the y position is dummy + const x = firstDoc ? 0 : NumCast(parentDoc.current.x) + freeformRenderSize + offsetX; + const initialY = firstDoc ? 500 : 0; + + const newImg = Docs.Create.ImageDocument(source, { + x: x, + y: initialY, + _height: freeformRenderSize, + _width: freeformRenderSize, + data_nativeWidth: result.nativeWidth, + data_nativeHeight: result.nativeHeight, + }); + + childrenDocs.current.push(newImg); + adjustImgPositions(); + Doc.AddDocToList(newCollectionRef.current, undefined, newImg); + DocUtils.MakeLink(parentDoc.current, newImg, { link_relationship: 'Image Edit' }); + return newImg; + }; + const onSave = async () => { - if (!currImg.current || !imageRootDoc) return; + if (!currImg.current || !originalImg.current || !imageRootDoc) return; try { - const src = currImg.current.src; - console.log(src); - const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [src] }); - const source = Utils.prepend(result.accessPaths.agnostic.client); - console.log(source); - const newImg = Docs.Create.ImageDocument(source, { - x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + 20, - y: NumCast(imageRootDoc.y), - _height: freeformRenderSize, - _width: freeformRenderSize, - data_nativeWidth: result.nativeWidth, - data_nativeHeight: result.nativeHeight, - }); - - addDoc?.(newImg); + if (!newCollectionRef.current) { + // create new collection and add it to the view + newCollectionRef.current = Docs.Create.FreeformDocument([], { x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + 20, y: NumCast(imageRootDoc.y), _width: 1000, _height: 1000 }); + addDoc?.(newCollectionRef.current); + const originalSrc = originalImg.current.src; + await createNewImgDoc(originalImg.current, true); + } + CollectionDockingView.AddSplit(Doc.MakeCopy(DocCast(Doc.UserDoc().emptyPane)), OpenWhereMod.right); + // CollectionDockingView.AddSplit(newCollection,OpenWhere.inParent) + // mindmapping + // this.props.addDocTab(); + // Create link between prompt and image - DocUtils.MakeLink(imageRootDoc, newImg, { link_relationship: 'Image Edit' }); - console.log('done'); } catch (err) { console.log(err); } @@ -361,9 +411,9 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
{/* Edits box */}
- {edits.map(edit => ( + {edits.map((edit, i) => ( Date: Thu, 6 Jul 2023 03:09:11 -0400 Subject: blind, need to test --- .../views/nodes/generativeFill/GenerativeFill.tsx | 40 +++++++++++++--------- .../nodes/generativeFill/GenerativeFillButtons.tsx | 2 +- 2 files changed, 25 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index 5e7b4fdca..0ebf370c0 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -77,6 +77,9 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD // early stage properly, likely will get rid of const freeformPosition = useRef([0, 0]); + // which urls were already saved to canvas + const savedSrcs = useRef([]); + // references to keep track of tree structure const newCollectionRef = useRef(null); const parentDoc = useRef(null); @@ -243,16 +246,18 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD const ctx = ImageUtility.getCanvasContext(canvasRef); if (!ctx) return; setLoading(true); + // need to adjust later try { const maskBlob = await ImageUtility.canvasToBlob(canvas); const imgBlob = await ImageUtility.canvasToBlob(ImageUtility.getCanvasImg(img)); - const res = await ImageUtility.getEdit(imgBlob, maskBlob, input !== '' ? input + ' in the same style' : 'Fill in the image in the same style', 2); // const res = await ImageUtility.mockGetEdit(img.src); + parentDoc.current = childrenDocs.current[childrenDocs.current.length - 1]; + childrenDocs.current = []; + originalImg.current = currImg.current; const { urls } = res as APISuccess; const image = new Image(); image.src = urls[0]; - setLoading(false); setEdits(urls); ImageUtility.drawImgToCanvas(image, canvasRef); currImg.current = image; @@ -261,6 +266,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD } catch (err) { console.log(err); } + setLoading(false); }; // adjusts all the img positions to be aligned @@ -301,31 +307,34 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD }); childrenDocs.current.push(newImg); - adjustImgPositions(); + if (!firstDoc) { + DocUtils.MakeLink(parentDoc.current, newImg, { link_relationship: 'Image Edit', link_displayLine: true }); + adjustImgPositions(); + } Doc.AddDocToList(newCollectionRef.current, undefined, newImg); - DocUtils.MakeLink(parentDoc.current, newImg, { link_relationship: 'Image Edit' }); return newImg; }; + // need to maybe call on every img click, not just when the save btn is clicked const onSave = async () => { + setLoading(true); if (!currImg.current || !originalImg.current || !imageRootDoc) return; try { if (!newCollectionRef.current) { // create new collection and add it to the view newCollectionRef.current = Docs.Create.FreeformDocument([], { x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + 20, y: NumCast(imageRootDoc.y), _width: 1000, _height: 1000 }); addDoc?.(newCollectionRef.current); - const originalSrc = originalImg.current.src; await createNewImgDoc(originalImg.current, true); } - CollectionDockingView.AddSplit(Doc.MakeCopy(DocCast(Doc.UserDoc().emptyPane)), OpenWhereMod.right); + await createNewImgDoc(currImg.current, false); + // CollectionDockingView.AddSplit(Doc.MakeCopy(DocCast(Doc.UserDoc().emptyPane)), OpenWhereMod.right); // CollectionDockingView.AddSplit(newCollection,OpenWhere.inParent) - // mindmapping + // mind mapping // this.props.addDocTab(); - - // Create link between prompt and image } catch (err) { console.log(err); } + setLoading(false); }; return ( @@ -336,6 +345,9 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD { + if (newCollectionRef.current) { + CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right); + } MainView.Instance.setImageEditorOpen(false); MainView.Instance.setImageEditorSource(''); setEdits([]); @@ -381,12 +393,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD }}> - {/* { - setBrushStyle(BrushStyle.SUBTRACT); - }}> - - */} {/* Undo and Redo */} { @@ -409,7 +415,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
- {/* Edits box */} + {/* Edits thumbnails*/}
{edits.map((edit, i) => ( ))} + {/* Original img thumbnail */} {edits.length > 0 && (
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 20a176c58..212d24165 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -909,7 +909,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { try { diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 07b2afd91..e6b9cb382 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -48,9 +48,6 @@ export class AnchorMenu extends AntimodeMenu { @observable public Status: 'marquee' | 'annotation' | '' = ''; // GPT additions - @observable private GPTpopupText: string = ''; - @observable private loadingGPT: boolean = false; - @observable private showGPTPopup: boolean = false; @observable private GPTMode: GPTPopupMode = GPTPopupMode.SUMMARY; @observable private selectedText: string = ''; @observable private editorView?: EditorView; @@ -58,25 +55,11 @@ export class AnchorMenu extends AntimodeMenu { @observable private highlightRange: number[] | undefined; private selectionRange: number[] | undefined; - @action - setGPTPopupVis = (vis: boolean) => { - this.showGPTPopup = vis; - }; @action setGPTMode = (mode: GPTPopupMode) => { this.GPTMode = mode; }; - @action - setGPTPopupText = (txt: string) => { - this.GPTpopupText = txt; - }; - - @action - setLoading = (loading: boolean) => { - this.loadingGPT = loading; - }; - @action setHighlightRange(r: number[] | undefined) { this.highlightRange = r; @@ -130,19 +113,12 @@ export class AnchorMenu extends AntimodeMenu { componentDidMount() { this._disposer2 = reaction( () => this._opacity, - opacity => { - if (!opacity) { - this.setGPTPopupVis(false); - this.setGPTPopupText(''); - } - }, + opacity => {}, { fireImmediately: true } ); this._disposer = reaction( () => SelectionManager.Views().slice(), selected => { - this.setGPTPopupVis(false); - this.setGPTPopupText(''); AnchorMenu.Instance.fadeOut(true); } ); @@ -153,23 +129,22 @@ export class AnchorMenu extends AntimodeMenu { * @param e pointer down event */ gptSummarize = async (e: React.PointerEvent) => { + GPTPopup.Instance.setVisible(true); this.setHighlightRange(undefined); - this.setGPTPopupVis(true); this.setGPTMode(GPTPopupMode.SUMMARY); - this.setLoading(true); + GPTPopup.Instance.setLoading(true); try { const res = await gptAPICall(this.selectedText, GPTCallType.SUMMARY); if (res) { - this.setGPTPopupText(res); + GPTPopup.Instance.setText(res); } else { - this.setGPTPopupText('Something went wrong.'); + GPTPopup.Instance.setText('Something went wrong.'); } } catch (err) { console.error(err); } - - this.setLoading(false); + GPTPopup.Instance.setLoading(false); }; /** @@ -184,9 +159,9 @@ export class AnchorMenu extends AntimodeMenu { const fullText = state.doc.textBetween(0, this.editorView.state.doc.content.size, ' \n'); const selectedText = state.doc.textBetween(sel.from, sel.to); - this.setGPTPopupVis(true); + GPTPopup.Instance.setVisible(true); this.setGPTMode(GPTPopupMode.EDIT); - this.setLoading(true); + GPTPopup.Instance.setLoading(true); try { let res = await gptAPICall(selectedText, GPTCallType.EDIT); @@ -196,16 +171,16 @@ export class AnchorMenu extends AntimodeMenu { const resultText = fullText.slice(0, sel.from - 1) + res + fullText.slice(sel.to - 1); if (res) { - this.setGPTPopupText(resultText); + GPTPopup.Instance.setText(resultText); this.setHighlightRange([sel.from - 1, sel.from - 1 + res.length]); } else { - this.setGPTPopupText('Something went wrong.'); + GPTPopup.Instance.setText('Something went wrong.'); } } catch (err) { console.error(err); } - this.setLoading(false); + GPTPopup.Instance.setLoading(false); }; /** @@ -253,21 +228,18 @@ export class AnchorMenu extends AntimodeMenu { }; @computed get highlighter() { - return - } - tooltip={'Click to Highlight'} - onClick={this.highlightClicked} - colorPicker={this.highlightColor} - color={StrCast(Doc.UserDoc().userColor)} - /> - this.changeHighlightColor(color)} - size={Size.XSMALL} - /> - + return ( + + } + tooltip={'Click to Highlight'} + onClick={this.highlightClicked} + colorPicker={this.highlightColor} + color={StrCast(Doc.UserDoc().userColor)} + /> + this.changeHighlightColor(color)} size={Size.XSMALL} /> + + ); } @action changeHighlightColor = (color: string) => { @@ -312,12 +284,7 @@ export class AnchorMenu extends AntimodeMenu { this.Status === 'marquee' ? ( <> {this.highlighter} - } - color={StrCast(Doc.UserDoc().userColor)} - /> + } color={StrCast(Doc.UserDoc().userColor)} /> {/* GPT Summarize icon only shows up when text is highlighted, not on marquee selection*/} {AnchorMenu.Instance.StartCropDrag === unimplementedFunction && this.canSummarize() && ( Summarize with AI
}> @@ -326,7 +293,7 @@ export class AnchorMenu extends AntimodeMenu { )} - { callEditApi={this.gptEdit} replaceText={this.replaceText} mode={this.GPTMode} - /> + /> */} {AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : ( - } - color={StrCast(Doc.UserDoc().userColor)} - /> - )} - {this.canEdit() && ( - } - color={StrCast(Doc.UserDoc().userColor)} - /> + } color={StrCast(Doc.UserDoc().userColor)} /> )} - } - popup={} - color={StrCast(Doc.UserDoc().userColor)} - /> + {this.canEdit() && } color={StrCast(Doc.UserDoc().userColor)} />} + } popup={} color={StrCast(Doc.UserDoc().userColor)} /> {AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? null : ( - } - color={StrCast(Doc.UserDoc().userColor)} - /> + } color={StrCast(Doc.UserDoc().userColor)} /> )} ) : ( <> - {this.Delete !== returnFalse && } - color={StrCast(Doc.UserDoc().userColor)} - />} - {this.PinToPres !== returnFalse && } - color={StrCast(Doc.UserDoc().userColor)} - />} - {this.ShowTargetTrail !== returnFalse && } - color={StrCast(Doc.UserDoc().userColor)} - />} - {this.IsTargetToggler !== returnFalse && } - color={StrCast(Doc.UserDoc().userColor)} - />} + {this.Delete !== returnFalse && } color={StrCast(Doc.UserDoc().userColor)} />} + {this.PinToPres !== returnFalse && } color={StrCast(Doc.UserDoc().userColor)} />} + {this.ShowTargetTrail !== returnFalse && } color={StrCast(Doc.UserDoc().userColor)} />} + {this.IsTargetToggler !== returnFalse && ( + } + color={StrCast(Doc.UserDoc().userColor)} + /> + )} ); diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss index 44413ede7..2f0ff83e2 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.scss +++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss @@ -6,12 +6,6 @@ $button: #5b97ff; $highlightedText: #82e0ff; .summary-box { - display: flex; - flex-direction: column; - justify-content: space-between; - background-color: #ffffff; - box-shadow: 0 2px 5px #7474748d; - color: $textgrey; position: fixed; bottom: 10px; right: 10px; @@ -21,6 +15,12 @@ $highlightedText: #82e0ff; padding: 15px; padding-bottom: 0; z-index: 999; + display: flex; + flex-direction: column; + justify-content: space-between; + background-color: #ffffff; + box-shadow: 0 2px 5px #7474748d; + color: $textgrey; .summary-heading { display: flex; diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index 8bd060d4f..9f28cb5d1 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -5,56 +5,96 @@ import { observer } from 'mobx-react'; import ReactLoading from 'react-loading'; import Typist from 'react-typist'; import { Doc } from '../../../../fields/Doc'; -import { Docs } from '../../../documents/Documents'; +import { DocUtils, Docs } from '../../../documents/Documents'; import './GPTPopup.scss'; +import { Button, IconButton } from 'browndash-components'; +import { StrCast } from '../../../../fields/Types'; export enum GPTPopupMode { SUMMARY, EDIT, } -interface GPTPopupProps { - visible: boolean; - text: string; - loading: boolean; - mode: GPTPopupMode; - callSummaryApi: (e: React.PointerEvent) => Promise; - callEditApi: (e: React.PointerEvent) => Promise; - replaceText: (replacement: string) => void; - highlightRange?: number[]; -} +interface GPTPopupProps {} @observer export class GPTPopup extends React.Component { static Instance: GPTPopup; @observable - private done: boolean = false; + public visible: boolean = false; + @action + public setVisible = (vis: boolean) => { + this.visible = vis; + }; @observable - private sidebarId: string = ''; + public loading: boolean = false; + @action + public setLoading = (loading: boolean) => { + this.loading = loading; + }; + @observable + public text: string = ''; + @action + public setText = (text: string) => { + this.text = text; + }; + @observable + public mode: GPTPopupMode = GPTPopupMode.SUMMARY; + @action + public setMode = (mode: GPTPopupMode) => { + this.mode = mode; + }; + @observable + public highlightRange: number[] = []; + @action callSummaryApi = () => {}; + @action callEditApi = () => {}; + @action replaceText = (replacement: string) => {}; + + @observable + private done: boolean = false; @action public setDone = (done: boolean) => { this.done = done; }; + @observable + private sidebarId: string = ''; @action public setSidebarId = (id: string) => { this.sidebarId = id; }; + // reference + // if (focusNode) { + // const anchor = srcWeb?.ComponentView?.getAnchor?.(true); + // anchor && DocUtils.MakeLink(htmlDoc, anchor, {}); + // } + @observable + private pdfAnchor: Doc | undefined; + @action + public setPdfAnchor = (anchor: Doc) => { + this.pdfAnchor = anchor; + }; + public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false; /** * Transfers the summarization text to a sidebar annotation text document. */ private transferToText = () => { - const newDoc = Docs.Create.TextDocument(this.props.text.trim(), { + const newDoc = Docs.Create.TextDocument(this.text.trim(), { _width: 200, _height: 50, _layout_fitWidth: true, _layout_autoHeight: true, }); this.addDoc(newDoc, this.sidebarId); + if (this.pdfAnchor) { + DocUtils.MakeLink(newDoc, this.pdfAnchor, { + link_relationship: 'GPT Summary', + }); + } }; constructor(props: GPTPopupProps) { @@ -63,7 +103,7 @@ export class GPTPopup extends React.Component { } componentDidUpdate = () => { - if (this.props.loading) { + if (this.loading) { this.setDone(false); } }; @@ -73,10 +113,10 @@ export class GPTPopup extends React.Component {
{this.heading('SUMMARY')}
- {!this.props.loading && + {!this.loading && (!this.done ? ( { @@ -84,18 +124,18 @@ export class GPTPopup extends React.Component { this.setDone(true); }, 500); }}> - {this.props.text} + {this.text} ) : ( - this.props.text + this.text ))}
- {!this.props.loading && ( + {!this.loading && (
{this.done ? ( <> - + */} + } color={StrCast(Doc.UserDoc().userVariantColor)} /> + @@ -174,14 +216,14 @@ export class GPTPopup extends React.Component { heading = (headingText: string) => (
- {this.props.loading && } + {this.loading && }
); render() { return ( -
- {this.props.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.editBox()} +
+ {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.editBox()}
); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 0fd93868a..4fc31ffe3 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -423,6 +423,11 @@ export class PDFViewer extends React.Component { // Changing which document to add the annotation to (the currently selected PDF) GPTPopup.Instance.setSidebarId('data_sidebar'); + const anchor = this._getAnchor(undefined, false); + if (anchor) { + console.log(anchor); + GPTPopup.Instance.setPdfAnchor(anchor); + } GPTPopup.Instance.addDoc = this.props.sidebarAddDoc; }; -- cgit v1.2.3-70-g09d2 From fec79d2b5b8feb361e489c9ee41ee720507d0806 Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Thu, 27 Jul 2023 13:23:42 -0400 Subject: changes --- report.20230720.011057.28881.0.001.json | 1269 ++++++++++++++++++++ src/client/apis/gpt/GPT.ts | 28 +- src/client/util/DragManager.ts | 4 +- src/client/views/collections/TabDocView.tsx | 6 +- .../CollectionFreeFormLinkView.tsx | 8 +- src/client/views/global/globalScripts.ts | 50 +- src/client/views/nodes/WebBox.tsx | 5 + .../views/nodes/formattedText/FormattedTextBox.tsx | 89 +- src/client/views/pdf/AnchorMenu.tsx | 21 +- src/client/views/pdf/GPTPopup/GPTPopup.scss | 52 + src/client/views/pdf/GPTPopup/GPTPopup.tsx | 129 +- src/client/views/pdf/PDFViewer.tsx | 3 +- 12 files changed, 1530 insertions(+), 134 deletions(-) create mode 100644 report.20230720.011057.28881.0.001.json (limited to 'src') diff --git a/report.20230720.011057.28881.0.001.json b/report.20230720.011057.28881.0.001.json new file mode 100644 index 000000000..5c72c0c91 --- /dev/null +++ b/report.20230720.011057.28881.0.001.json @@ -0,0 +1,1269 @@ + +{ + "header": { + "reportVersion": 1, + "event": "Allocation failed - JavaScript heap out of memory", + "trigger": "FatalError", + "filename": "report.20230720.011057.28881.0.001.json", + "dumpEventTime": "2023-07-20T01:10:57Z", + "dumpEventTimeStamp": "1689829857721", + "processId": 28881, + "cwd": "/Users/smallwhale/Desktop/Projects/Dash-Web", + "commandLine": [ + "/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node", + "--max-old-space-size=2048", + "/Users/smallwhale/Desktop/Projects/Dash-Web/node_modules/ts-node-dev/lib/wrap.js", + "/Users/smallwhale/Desktop/Projects/Dash-Web/node_modules/fork-ts-checker-webpack-plugin/lib/service.js" + ], + "nodejsVersion": "v12.16.0", + "wordSize": 64, + "arch": "x64", + "platform": "darwin", + "componentVersions": { + "node": "12.16.0", + "v8": "7.8.279.23-node.31", + "uv": "1.34.0", + "zlib": "1.2.11", + "brotli": "1.0.7", + "ares": "1.15.0", + "modules": "72", + "nghttp2": "1.40.0", + "napi": "5", + "llhttp": "2.0.4", + "http_parser": "2.9.3", + "openssl": "1.1.1d", + "cldr": "35.1", + "icu": "64.2", + "tz": "2019c", + "unicode": "12.1" + }, + "release": { + "name": "node", + "lts": "Erbium", + "headersUrl": "https://nodejs.org/download/release/v12.16.0/node-v12.16.0-headers.tar.gz", + "sourceUrl": "https://nodejs.org/download/release/v12.16.0/node-v12.16.0.tar.gz" + }, + "osName": "Darwin", + "osRelease": "22.4.0", + "osVersion": "Darwin Kernel Version 22.4.0: Mon Mar 6 21:01:02 PST 2023; root:xnu-8796.101.5~3/RELEASE_ARM64_T8112", + "osMachine": "x86_64", + "cpus": [ + { + "model": "Apple M2", + "speed": 2400, + "user": 40342390, + "nice": 0, + "sys": 32818540, + "idle": 263532590, + "irq": 0 + }, + { + "model": "Apple M2", + "speed": 2400, + "user": 37882510, + "nice": 0, + "sys": 28750670, + "idle": 270292020, + "irq": 0 + }, + { + "model": "Apple M2", + "speed": 2400, + "user": 32806560, + "nice": 0, + "sys": 24772270, + "idle": 279705870, + "irq": 0 + }, + { + "model": "Apple M2", + "speed": 2400, + "user": 28917140, + "nice": 0, + "sys": 21505900, + "idle": 287180070, + "irq": 0 + }, + { + "model": "Apple M2", + "speed": 2400, + "user": 52175300, + "nice": 0, + "sys": 11670040, + "idle": 274363170, + "irq": 0 + }, + { + "model": "Apple M2", + "speed": 2400, + "user": 30406640, + "nice": 0, + "sys": 8969580, + "idle": 298959950, + "irq": 0 + }, + { + "model": "Apple M2", + "speed": 2400, + "user": 21936370, + "nice": 0, + "sys": 6476860, + "idle": 310114030, + "irq": 0 + }, + { + "model": "Apple M2", + "speed": 2400, + "user": 16048180, + "nice": 0, + "sys": 4956530, + "idle": 317649610, + "irq": 0 + } + ], + "networkInterfaces": [ + { + "name": "lo0", + "internal": true, + "mac": "00:00:00:00:00:00", + "address": "127.0.0.1", + "netmask": "255.0.0.0", + "family": "IPv4" + }, + { + "name": "lo0", + "internal": true, + "mac": "00:00:00:00:00:00", + "address": "::1", + "netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + "family": "IPv6", + "scopeid": 0 + }, + { + "name": "lo0", + "internal": true, + "mac": "00:00:00:00:00:00", + "address": "fe80::1", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 1 + }, + { + "name": "anpi0", + "internal": false, + "mac": "42:10:d5:5b:93:9e", + "address": "fe80::4010:d5ff:fe5b:939e", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 4 + }, + { + "name": "anpi1", + "internal": false, + "mac": "42:10:d5:5b:93:9f", + "address": "fe80::4010:d5ff:fe5b:939f", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 5 + }, + { + "name": "en0", + "internal": false, + "mac": "9c:3e:53:8e:59:0e", + "address": "fe80::2b:dde:e859:46b8", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 12 + }, + { + "name": "en0", + "internal": false, + "mac": "9c:3e:53:8e:59:0e", + "address": "192.168.1.154", + "netmask": "255.255.255.0", + "family": "IPv4" + }, + { + "name": "en0", + "internal": false, + "mac": "9c:3e:53:8e:59:0e", + "address": "2600:4040:572e:2f00:18fe:e894:e23c:f4c2", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 0 + }, + { + "name": "en0", + "internal": false, + "mac": "9c:3e:53:8e:59:0e", + "address": "2600:4040:572e:2f00:c182:d0ad:6e64:1fae", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 0 + }, + { + "name": "awdl0", + "internal": false, + "mac": "b6:6c:9a:3c:a7:93", + "address": "fe80::b46c:9aff:fe3c:a793", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 13 + }, + { + "name": "llw0", + "internal": false, + "mac": "b6:6c:9a:3c:a7:93", + "address": "fe80::b46c:9aff:fe3c:a793", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 14 + }, + { + "name": "utun0", + "internal": false, + "mac": "00:00:00:00:00:00", + "address": "fe80::be27:76ec:8f11:5429", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 15 + }, + { + "name": "utun1", + "internal": false, + "mac": "00:00:00:00:00:00", + "address": "fe80::ce81:b1c:bd2c:69e", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 16 + }, + { + "name": "utun2", + "internal": false, + "mac": "00:00:00:00:00:00", + "address": "fe80::7707:8801:aef4:1907", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 17 + }, + { + "name": "utun3", + "internal": false, + "mac": "00:00:00:00:00:00", + "address": "fe80::a4f8:a3ce:851e:23dd", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 18 + }, + { + "name": "utun4", + "internal": false, + "mac": "00:00:00:00:00:00", + "address": "fe80::8d47:6641:b300:bde8", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 19 + }, + { + "name": "utun5", + "internal": false, + "mac": "00:00:00:00:00:00", + "address": "fe80::7c36:c665:d5f7:49d7", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 20 + }, + { + "name": "utun6", + "internal": false, + "mac": "00:00:00:00:00:00", + "address": "fe80::10d8:db7a:d714:7746", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 21 + } + ], + "host": "Sophies-MBP" + }, + "javascriptStack": { + "message": "No stack.", + "stack": [ + "Unavailable." + ] + }, + "nativeStack": [ + { + "pc": "0x000000010015c8ca", + "symbol": "report::TriggerNodeReport(v8::Isolate*, node::Environment*, char const*, char const*, std::__1::basic_string, std::__1::allocator> const&, v8::Local) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]" + }, + { + "pc": "0x0000000100080f3e", + "symbol": "node::OnFatalError(char const*, char const*) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]" + }, + { + "pc": "0x0000000100185467", + "symbol": "v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]" + }, + { + "pc": "0x0000000100185403", + "symbol": "v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]" + }, + { + "pc": "0x000000010030b5f5", + "symbol": "v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]" + }, + { + "pc": "0x000000010030ccc4", + "symbol": "v8::internal::Heap::RecomputeLimits(v8::internal::GarbageCollector) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]" + }, + { + "pc": "0x0000000100309b37", + "symbol": "v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]" + }, + { + "pc": "0x0000000100307afd", + "symbol": "v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]" + }, + { + "pc": "0x00000001003132ba", + "symbol": "v8::internal::Heap::AllocateRawWithLightRetry(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]" + }, + { + "pc": "0x0000000100313341", + "symbol": "v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]" + }, + { + "pc": "0x00000001002e065b", + "symbol": "v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]" + }, + { + "pc": "0x0000000100618a18", + "symbol": "v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]" + }, + { + "pc": "0x0000000100950c19", + "symbol": "Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]" + }, + { + "pc": "0x00000001008d2291", + "symbol": "Builtins_FastNewObject [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]" + } + ], + "javascriptHeap": { + "totalMemory": 2152435712, + "totalCommittedMemory": 2150538320, + "usedMemory": 2139294040, + "availableMemory": 47735856, + "memoryLimit": 2197815296, + "heapSpaces": { + "read_only_space": { + "memorySize": 262144, + "committedMemory": 33088, + "capacity": 32808, + "used": 32808, + "available": 0 + }, + "new_space": { + "memorySize": 2097152, + "committedMemory": 1236968, + "capacity": 1047456, + "used": 203328, + "available": 844128 + }, + "old_space": { + "memorySize": 1956003840, + "committedMemory": 1955767472, + "capacity": 1948209048, + "used": 1947744360, + "available": 464688 + }, + "code_space": { + "memorySize": 14061568, + "committedMemory": 13610624, + "capacity": 12121760, + "used": 12121760, + "available": 0 + }, + "map_space": { + "memorySize": 1576960, + "committedMemory": 1456120, + "capacity": 1257120, + "used": 1257120, + "available": 0 + }, + "large_object_space": { + "memorySize": 178384896, + "committedMemory": 178384896, + "capacity": 177931880, + "used": 177931880, + "available": 0 + }, + "code_large_object_space": { + "memorySize": 49152, + "committedMemory": 49152, + "capacity": 2784, + "used": 2784, + "available": 0 + }, + "new_large_object_space": { + "memorySize": 0, + "committedMemory": 0, + "capacity": 1047456, + "used": 0, + "available": 1047456 + } + } + }, + "resourceUsage": { + "userCpuSeconds": 312.946, + "kernelCpuSeconds": 137.981, + "cpuConsumptionPercent": 62.0257, + "maxRss": 2062897119232, + "pageFaults": { + "IORequired": 658, + "IONotRequired": 43579208 + }, + "fsActivity": { + "reads": 0, + "writes": 0 + } + }, + "libuv": [ + ], + "environmentVariables": { + "npm_config_save_dev": "", + "npm_config_legacy_bundling": "", + "npm_config_dry_run": "", + "npm_package_dependencies_translate_google_api": "^1.0.4", + "npm_package_dependencies_request": "^2.88.2", + "npm_package_dependencies_express_flash": "0.0.2", + "npm_package_dependencies__fortawesome_fontawesome_svg_core": "^6.3.0", + "NVM_INC": "/Users/smallwhale/.nvm/versions/node/v12.16.0/include/node", + "npm_config_viewer": "man", + "npm_config_only": "", + "npm_config_commit_hooks": "true", + "npm_config_browser": "", + "npm_package_gitHead": "e11aa60b774d457cb016bb0f375ce092f0a733af", + "npm_package_dependencies_webpack_dev_middleware": "^5.3.1", + "npm_package_dependencies_webpack_cli": "^4.10.0", + "npm_package_devDependencies_prettier": "^2.7.1", + "npm_package_devDependencies_awesome_typescript_loader": "^5.2.1", + "npm_package_devDependencies__types_archiver": "^3.1.1", + "MANPATH": "/Users/smallwhale/.nvm/versions/node/v12.16.0/share/man:/Users/smallwhale/.nvm/versions/node/v18.16.0/share/man:/opt/homebrew/share/man:/usr/share/man:/usr/local/share/man:/Users/smallwhale/.nvm/versions/node/v18.16.0/share/man:/opt/homebrew/share/man::", + "npm_config_also": "", + "npm_package_dependencies_react_jsx_parser": "^1.29.0", + "npm_package_dependencies_mongoose": "^5.13.14", + "npm_package_dependencies_connect_flash": "^0.1.1", + "npm_package_browser_child_process": "false", + "npm_config_sign_git_commit": "", + "npm_config_rollback": "true", + "npm_package_dependencies_material_ui": "^0.20.2", + "npm_package_devDependencies__types_sharp": "^0.23.1", + "npm_package_devDependencies__types_passport_local": "^1.0.34", + "npm_package_devDependencies__types_dotenv": "^6.1.1", + "npm_package_devDependencies__types_cookie_parser": "^1.4.2", + "TERM_PROGRAM": "vscode", + "NODE": "/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node", + "npm_config_usage": "", + "npm_config_audit": "true", + "npm_package_dependencies_reveal_js": "^4.3.0", + "npm_package_dependencies_process": "^0.11.10", + "npm_package_dependencies_pdfjs": "^2.4.7", + "npm_package_dependencies_html_to_image": "^0.1.3", + "npm_package_devDependencies_file_loader": "^3.0.1", + "npm_package_devDependencies__types_express_flash": "0.0.0", + "npm_package_scripts_monitor": "cross-env MONITORED=true NODE_OPTIONS=--max_old_space_size=4096 ts-node src/server/index.ts", + "INIT_CWD": "/Users/smallwhale/Desktop/Projects/Dash-Web", + "npm_package_dependencies_rehype_raw": "^6.1.1", + "npm_package_dependencies_react_audio_waveform": "0.0.5", + "npm_package_dependencies_path_browserify": "^1.0.1", + "npm_package_dependencies_nodemailer": "^5.1.1", + "npm_package_dependencies_axios": "^0.19.2", + "npm_package_devDependencies_typescript": "^4.7.4", + "NVM_CD_FLAGS": "-q", + "npm_config_globalignorefile": "/Users/smallwhale/.nvm/versions/node/v12.16.0/etc/npmignore", + "npm_package_dependencies_react_grid_layout": "^1.3.4", + "npm_package_dependencies_prosemirror_find_replace": "^0.9.0", + "npm_package_dependencies_normalize_css": "^8.0.1", + "npm_package_devDependencies_mocha": "^5.2.0", + "npm_package_devDependencies__types_express_session": "^1.17.5", + "TERM": "xterm-256color", + "SHELL": "/bin/zsh", + "npm_config_shell": "/bin/zsh", + "npm_config_maxsockets": "50", + "npm_config_init_author_url": "", + "npm_package_dependencies_prosemirror_dev_tools": "^3.1.0", + "npm_package_dependencies_p_limit": "^2.2.0", + "npm_package_dependencies_bson": "^4.6.1", + "npm_package_dependencies__types_dom_speech_recognition": "0.0.1", + "npm_package_dependencies__emotion_styled": "^11.11.0", + "npm_package_devDependencies_style_loader": "^0.23.1", + "npm_package_devDependencies__types_react_datepicker": "^3.1.8", + "npm_config_shrinkwrap": "true", + "npm_config_parseable": "", + "npm_config_metrics_registry": "https://registry.npmjs.org/", + "npm_package_dependencies_xregexp": "^4.4.1", + "npm_package_dependencies_shelljs": "^0.8.5", + "npm_package_dependencies_bezier_curve": "^1.0.0", + "npm_package_dependencies__mui_icons_material": "^5.11.16", + "npm_package_devDependencies_tslint": "^5.20.1", + "npm_package_devDependencies__types_react_transition_group": "^4.4.5", + "npm_package_scripts_tsc": "tsc", + "HOMEBREW_REPOSITORY": "/opt/homebrew", + "TMPDIR": "/var/folders/bv/1xck6d7j7bz14bvhgdsllbj40000gn/T/", + "npm_config_timing": "", + "npm_config_init_license": "ISC", + "npm_package_dependencies_socket_io": "^2.5.0", + "npm_package_dependencies_probe_image_size": "^4.0.0", + "npm_package_dependencies_canvas": "^2.9.3", + "npm_package_dependencies__hig_theme_data": "^2.23.1", + "npm_package_devDependencies__types_react_select": "^3.1.2", + "npm_package_devDependencies__types_prosemirror_model": "^1.16.1", + "npm_config_if_present": "", + "npm_package_dependencies_typescript_collections": "^1.3.3", + "npm_package_dependencies_rimraf": "^3.0.0", + "npm_package_dependencies_react_autosuggest": "^9.4.3", + "npm_package_dependencies_flexlayout_react": "^0.3.11", + "npm_package_dependencies_find_in_files": "^0.5.0", + "npm_package_devDependencies__types_chai": "^4.3.0", + "TERM_PROGRAM_VERSION": "1.79.2", + "npm_package_dependencies_prosemirror_inputrules": "^1.1.3", + "npm_package_dependencies_bcrypt_nodejs": "0.0.3", + "npm_package_dependencies_async": "^2.6.2", + "npm_config_sign_git_tag": "", + "npm_config_init_author_email": "", + "npm_config_cache_max": "Infinity", + "npm_package_dependencies_uuid": "^3.4.0", + "npm_package_dependencies_supercluster": "^7.1.4", + "npm_package_dependencies_remark_gfm": "^3.0.1", + "npm_package_dependencies_connect_mongo": "^2.0.3", + "npm_package_dependencies_browser_assert": "^1.2.1", + "npm_package_devDependencies_sass_loader": "^7.3.1", + "ZDOTDIR": "/Users/smallwhale", + "ORIGINAL_XDG_CURRENT_DESKTOP": "undefined", + "MallocNanoZone": "0", + "npm_config_preid": "", + "npm_config_long": "", + "npm_config_local_address": "", + "npm_config_git_tag_version": "true", + "npm_config_cert": "", + "npm_package_dependencies_js_datepicker": "^4.6.6", + "npm_package_devDependencies__types_webpack_hot_middleware": "^2.25.6", + "npm_package_devDependencies__types_mongodb": "^3.6.20", + "npm_package_devDependencies__types_mocha": "^5.2.6", + "TERM_SESSION_ID": "F2049B14-CDC7-4078-A2C4-82A8C869F43E", + "npm_config_registry": "https://registry.npmjs.org/", + "npm_config_noproxy": "", + "npm_config_fetch_retries": "2", + "npm_package_dependencies_react_compound_slider": "^2.5.0", + "npm_package_dependencies_prosemirror_history": "^1.2.0", + "npm_package_devDependencies__types_react_color": "^2.17.6", + "npm_package_devDependencies__types_google_maps_react": "^2.0.5", + "npm_package_devDependencies__types_color": "^3.0.3", + "npm_package_dependencies_react_dom": "^18.2.0", + "npm_package_dependencies_passport_local": "^1.0.0", + "npm_package_dependencies__octokit_core": "^4.0.4", + "npm_package_devDependencies__types_async": "^2.4.1", + "npm_package_scripts_debug": "cross-env NODE_OPTIONS=--max_old_space_size=8192 ts-node-dev --transpile-only --inspect -- src/server/index.ts", + "npm_package_scripts_oldstart": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug -- src/server/index.ts", + "npm_config_versions": "", + "npm_config_message": "%s", + "npm_config_key": "", + "npm_package_readmeFilename": "README.md", + "npm_package_dependencies_react_refresh_typescript": "^2.0.7", + "npm_package_dependencies_image_size": "^0.7.5", + "npm_package_dependencies_html_to_text": "^5.1.1", + "npm_package_dependencies_express_validator": "^5.3.1", + "npm_package_devDependencies_eslint_plugin_jsx_a11y": "^6.6.0", + "npm_package_node_child_process": "empty", + "npm_package_dependencies_react_resizable_rotatable_draggable": "^0.2.0", + "npm_package_dependencies_got": "^12.0.1", + "npm_package_dependencies__types_d3_color": "^2.0.3", + "npm_package_devDependencies_webpack": "^5.69.1", + "npm_package_devDependencies__types_nodemailer": "^4.6.6", + "npm_package_description": "Install Node.js, then, from the project directory, run", + "NVM_DIR": "/Users/smallwhale/.nvm", + "USER": "smallwhale", + "npm_package_dependencies__types_d3_scale": "^3.3.2", + "npm_package_devDependencies_dotenv": "^8.6.0", + "npm_package_devDependencies__types_react": "^18.0.15", + "npm_package_devDependencies__types_prosemirror_transform": "^1.1.5", + "npm_package_devDependencies__types_prosemirror_history": "^1.0.3", + "npm_package_dependencies_readline": "^1.3.0", + "npm_package_dependencies__types_supercluster": "^7.1.0", + "COMMAND_MODE": "unix2003", + "npm_config_globalconfig": "/Users/smallwhale/.nvm/versions/node/v12.16.0/etc/npmrc", + "npm_package_dependencies_depcheck": "^0.9.2", + "npm_package_dependencies__types_web": "0.0.53", + "npm_config_prefer_online": "", + "npm_config_logs_max": "10", + "npm_config_always_auth": "", + "npm_package_dependencies_react_icons": "^4.3.1", + "npm_package_dependencies_passport_google_oauth20": "^2.0.0", + "npm_package_devDependencies_webpack_dev_server": "^3.11.3", + "npm_package_devDependencies__types_brotli": "^1.3.1", + "npm_package_dependencies_url_loader": "^1.1.2", + "npm_package_dependencies_stream_browserify": "^3.0.0", + "npm_package_dependencies_prosemirror_transform": "^1.3.4", + "npm_package_dependencies_lodash": "^4.17.21", + "npm_package_dependencies_i": "^0.3.7", + "npm_package_devDependencies_tslint_loader": "^3.6.0", + "SSH_AUTH_SOCK": "/private/tmp/com.apple.launchd.iMTrb4REX6/Listeners", + "npm_package_dependencies_words_to_numbers": "^1.5.1", + "npm_package_dependencies_valid_url": "^1.0.9", + "npm_package_dependencies_styled_components": "^4.4.1", + "npm_package_dependencies_csv_parser": "^3.0.0", + "npm_package_dependencies_class_transformer": "^0.2.0", + "npm_package_devDependencies_eslint": "^8.36.0", + "npm_package_devDependencies__types_prosemirror_inputrules": "^1.0.4", + "npm_package_devDependencies__types_express": "^4.17.13", + "__CF_USER_TEXT_ENCODING": "0x1F5:0x0:0x0", + "npm_execpath": "/Users/smallwhale/.nvm/versions/node/v12.16.0/lib/node_modules/npm/bin/npm-cli.js", + "npm_config_global_style": "", + "npm_config_cache_lock_retries": "10", + "npm_package_dependencies_wikijs": "^6.3.3", + "npm_package_dependencies_bluebird": "^3.7.2", + "npm_package_devDependencies__types_react_typist": "^2.0.3", + "npm_config_update_notifier": "true", + "npm_config_cafile": "", + "npm_package_dependencies_util": "^0.12.4", + "npm_package_dependencies_raw_loader": "^1.0.0", + "npm_package_dependencies_https_browserify": "^1.0.0", + "npm_package_dependencies_brotli": "^1.3.3", + "npm_package_dependencies__mui_material": "^5.13.1", + "npm_package_dependencies__fortawesome_react_fontawesome": "^0.2.0", + "npm_package_devDependencies__types_passport_google_oauth20": "^2.0.11", + "npm_package_dependencies_cors": "^2.8.5", + "npm_package_dependencies_bezier_js": "^4.1.1", + "npm_package_dependencies__fortawesome_free_brands_svg_icons": "^6.3.0", + "npm_config_heading": "npm", + "npm_config_audit_level": "low", + "npm_package_dependencies_chrome": "^0.1.0", + "npm_package_dependencies__react_three_fiber": "^6.2.3", + "npm_package_devDependencies_eslint_plugin_prettier": "^4.2.1", + "npm_package_devDependencies_copy_webpack_plugin": "^4.6.0", + "npm_package_devDependencies__types_react_measure": "^2.0.8", + "npm_package_devDependencies__types_react_dom": "^18.0.6", + "npm_package_devDependencies__types_mobile_detect": "^1.3.4", + "npm_config_searchlimit": "20", + "npm_config_read_only": "", + "npm_config_offline": "", + "npm_config_fetch_retry_mintimeout": "10000", + "npm_package_dependencies_react_typist": "^2.0.5", + "npm_package_dependencies_mobx_react_devtools": "^6.1.1", + "npm_package_dependencies_md5_file": "^5.0.0", + "npm_package_dependencies_forever_agent": "^0.6.1", + "npm_package_devDependencies__types_xregexp": "^4.4.0", + "npm_package_devDependencies__types_typescript": "^2.0.0", + "npm_package_devDependencies__types_request": "^2.48.8", + "npm_package_devDependencies__types_prosemirror_commands": "^1.0.4", + "npm_config_json": "", + "npm_config_access": "", + "npm_config_argv": "{\"remain\":[],\"cooked\":[\"start\"],\"original\":[\"start\"]}", + "npm_package_dependencies__fortawesome_free_solid_svg_icons": "^6.3.0", + "npm_package_devDependencies__types_socket_io": "^2.1.13", + "PATH": "/Users/smallwhale/.nvm/versions/node/v12.16.0/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/Users/smallwhale/Desktop/Projects/Dash-Web/node_modules/.bin:/Users/smallwhale/.nvm/versions/node/v12.16.0/bin:/Users/smallwhale/.nvm/versions/node/v18.16.0/bin:/Library/Frameworks/Python.framework/Versions/3.11/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/Users/smallwhale/.nvm/versions/node/v18.16.0/bin:/Library/Frameworks/Python.framework/Versions/3.11/bin:/opt/homebrew/bin:/opt/homebrew/sbin", + "npm_config_allow_same_version": "", + "npm_package_dependencies_webrtc_adapter": "^7.7.1", + "npm_package_dependencies_react_reveal": "^1.2.2", + "npm_package_dependencies_prosemirror_schema_list": "^1.1.6", + "npm_package_dependencies__material_ui_core": "^4.12.3", + "npm_package_devDependencies__types_rimraf": "^2.0.5", + "npm_package_devDependencies__types_connect_flash": "0.0.34", + "npm_config_https_proxy": "", + "npm_config_engine_strict": "", + "npm_config_description": "true", + "npm_package_dependencies_pug": "^2.0.4", + "npm_package_dependencies_prosemirror_keymap": "^1.1.5", + "npm_package_dependencies_pdfjs_dist": "^2.14.305", + "npm_package_dependencies_mobile_detect": "^1.4.5", + "npm_package_dependencies_image_size_stream": "^1.1.0", + "npm_package_dependencies_golden_layout": "^1.5.9", + "npm_package_dependencies_child_process": "^1.0.2", + "npm_package_dependencies__types_d3_axis": "^2.1.3", + "_": "/Users/smallwhale/Desktop/Projects/Dash-Web/node_modules/.bin/cross-env", + "LaunchInstanceID": "57D8446D-0E37-4DF2-8110-E0AA2FF019D8", + "npm_config_userconfig": "/Users/smallwhale/.npmrc", + "npm_config_init_module": "/Users/smallwhale/.npm-init.js", + "npm_package_dependencies__react_google_maps_api": "^2.7.0", + "USER_ZDOTDIR": "/Users/smallwhale", + "__CFBundleIdentifier": "com.microsoft.VSCode", + "npm_config_cidr": "", + "npm_package_dependencies_puppeteer": "^3.3.0", + "npm_package_dependencies_prosemirror_view": "^1.26.5", + "npm_package_dependencies_mongodb": "^3.7.3", + "npm_package_dependencies_google_auth_library": "^4.2.4", + "npm_package_dependencies_bootstrap": "^4.6.1", + "npm_package_devDependencies_eslint_config_airbnb": "^19.0.4", + "PWD": "/Users/smallwhale/Desktop/Projects/Dash-Web", + "npm_config_user": "501", + "npm_config_node_version": "12.16.0", + "npm_package_dependencies_node_sass": "^4.14.1", + "npm_package_dependencies_howler": "^2.2.3", + "npm_package_dependencies_expressjs": "^1.0.1", + "npm_package_dependencies_core_js": "^3.28.0", + "npm_package_dependencies_browndash_components": "0.0.82", + "npm_package_devDependencies_eslint_plugin_react_hooks": "^4.6.0", + "npm_package_devDependencies__types_lodash": "^4.14.179", + "npm_lifecycle_event": "start", + "npm_package_dependencies_react_table": "^6.11.5", + "npm_package_dependencies_react_loading": "^2.0.3", + "npm_package_dependencies_mobx": "^5.15.7", + "npm_package_dependencies_babel": "^6.23.0", + "npm_package_devDependencies_jsdom": "^15.2.1", + "npm_package_devDependencies_chai": "^4.3.6", + "npm_config_save": "true", + "npm_config_ignore_prepublish": "", + "npm_config_editor": "vi", + "npm_config_auth_type": "legacy", + "npm_package_dependencies_npm": "^6.14.18", + "npm_package_dependencies_node_stream_zip": "^1.15.0", + "npm_package_dependencies_image_data_uri": "^2.0.1", + "npm_package_scripts_start_release": "cross-env RELEASE=true NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts", + "npm_package_name": "dash", + "LANG": "en_US.UTF-8", + "npm_config_tag": "latest", + "npm_config_script_shell": "", + "npm_package_dependencies_query_string": "^6.14.1", + "npm_package_dependencies_mobx_utils": "^5.6.2", + "npm_package_dependencies_file_saver": "^2.0.5", + "npm_package_dependencies_body_parser": "^1.19.2", + "npm_package_dependencies__types_reveal": "^3.3.33", + "npm_package_devDependencies_eslint_plugin_import": "^2.26.0", + "npm_package_devDependencies__types_prosemirror_view": "^1.23.1", + "npm_config_progress": "true", + "npm_config_global": "", + "npm_config_before": "", + "npm_package_dependencies_xoauth2": "^1.2.0", + "npm_package_dependencies_standard_http_error": "^2.0.1", + "npm_package_dependencies_react_loader_spinner": "^5.3.4", + "npm_package_dependencies_http_browserify": "^1.7.0", + "npm_package_dependencies__types_d3_selection": "^2.0.1", + "npm_package_dependencies__hig_flyout": "^1.3.1", + "npm_package_devDependencies_fork_ts_checker_webpack_plugin": "^1.6.0", + "npm_package_scripts_build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 webpack --env production", + "npm_package_scripts_start": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug --transpile-only -- src/server/index.ts", + "npm_config_searchstaleness": "900", + "npm_config_optional": "true", + "npm_config_ham_it_up": "", + "npm_package_dependencies_sharp": "^0.23.4", + "npm_package_dependencies_rc_switch": "^1.9.2", + "npm_package_dependencies_googlephotos": "^0.2.5", + "npm_package_dependencies_exifr": "^7.1.3", + "npm_package_dependencies__types_google_maps": "^3.2.3", + "npm_package_dependencies__types_bezier_js": "^4.1.0", + "npm_package_dependencies__ffmpeg_core": "0.10.0", + "npm_package_devDependencies_ts_loader": "^5.3.3", + "npm_package_devDependencies__types_bcrypt_nodejs": "0.0.30", + "VSCODE_GIT_ASKPASS_EXTRA_ARGS": "--ms-enable-electron-run-as-node", + "XPC_FLAGS": "0x0", + "npm_config_save_prod": "", + "npm_config_force": "", + "npm_config_bin_links": "true", + "npm_package_devDependencies__types_youtube": "0.0.39", + "npm_config_searchopts": "", + "npm_package_dependencies_react_beautiful_dnd": "^13.1.0", + "npm_package_dependencies_jszip": "^3.7.1", + "npm_package_dependencies_csv_stringify": "^6.3.0", + "npm_package_devDependencies__types_react_icons": "^3.0.0", + "npm_config_node_gyp": "/Users/smallwhale/.nvm/versions/node/v12.16.0/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js", + "npm_config_depth": "Infinity", + "npm_package_dependencies_google_maps_react": "^2.0.6", + "npm_package_dependencies_express_session": "^1.17.2", + "npm_package_devDependencies_eslint_plugin_node": "^11.1.0", + "npm_package_devDependencies_eslint_config_prettier": "^8.5.0", + "npm_package_main": "index.js", + "npm_config_sso_poll_frequency": "500", + "npm_config_rebuild_bundle": "true", + "npm_package_dependencies_chart_js": "^3.8.0", + "npm_package_dependencies__emotion_react": "^11.11.0", + "npm_package_devDependencies__types_prosemirror_menu": "^1.0.6", + "npm_package_devDependencies__types_prosemirror_keymap": "^1.0.4", + "npm_package_devDependencies__types_pdfjs_dist": "^2.10.378", + "npm_package_devDependencies__types_exif": "^0.6.3", + "npm_package_version": "1.0.0", + "XPC_SERVICE_NAME": "0", + "npm_config_unicode": "true", + "VSCODE_INJECTION": "1", + "npm_package_dependencies_typescript_language_server": "^0.4.0", + "npm_package_dependencies_prosemirror_model": "^1.18.1", + "npm_package_dependencies__ffmpeg_ffmpeg": "0.10.0", + "SHLVL": "4", + "HOME": "/Users/smallwhale", + "npm_config_fetch_retry_maxtimeout": "60000", + "npm_package_dependencies_request_promise": "^4.2.6", + "npm_package_dependencies_react_markdown": "^8.0.3", + "npm_package_dependencies__hig_theme_context": "^2.1.3", + "npm_package_devDependencies__types_react_autosuggest": "^9.3.14", + "npm_package_devDependencies__types_mongoose": "^5.11.97", + "npm_package_devDependencies__types_d3": "^7.4.0", + "npm_package_devDependencies__types_animejs": "^2.0.2", + "npm_package_scripts_test": "mocha -r ts-node/register test/**/*.ts", + "VSCODE_GIT_ASKPASS_MAIN": "/Applications/Visual Studio Code.app/Contents/Resources/app/extensions/git/dist/askpass-main.js", + "npm_config_tag_version_prefix": "v", + "npm_config_strict_ssl": "true", + "npm_config_sso_type": "oauth", + "npm_config_scripts_prepend_node_path": "warn-only", + "npm_config_save_prefix": "^", + "npm_config_loglevel": "notice", + "npm_config_ca": "", + "npm_package_dependencies_three": "^0.127.0", + "npm_package_dependencies_openai": "^3.2.1", + "npm_package_dependencies_mobx_react": "^5.4.4", + "npm_package_dependencies_cookie_parser": "^1.4.6", + "npm_package_dependencies_adm_zip": "^0.4.16", + "npm_package_devDependencies_eslint_config_node": "^4.1.0", + "npm_config_save_exact": "", + "npm_config_group": "20", + "npm_config_fetch_retry_factor": "10", + "npm_config_dev": "", + "npm_package_devDependencies_webpack_hot_middleware": "^2.25.1", + "npm_package_devDependencies_cross_env": "^5.2.1", + "HOMEBREW_PREFIX": "/opt/homebrew", + "npm_config_version": "", + "npm_config_prefer_offline": "", + "npm_config_cache_lock_stale": "60000", + "npm_package_devDependencies__types_prosemirror_state": "^1.2.8", + "npm_package_devDependencies__types_body_parser": "^1.19.2", + "npm_config_otp": "", + "npm_config_cache_min": "10", + "npm_package_dependencies_react_color": "^2.19.3", + "npm_package_dependencies_d3": "^7.6.1", + "npm_package_devDependencies_ts_node": "^10.9.1", + "npm_package_devDependencies__types_react_grid_layout": "^1.3.2", + "npm_config_searchexclude": "", + "npm_config_cache": "/Users/smallwhale/.npm", + "npm_package_dependencies_tough_cookie": "^4.0.0", + "npm_package_dependencies_googleapis": "^40.0.0", + "npm_package_devDependencies__types_valid_url": "^1.0.3", + "npm_package_devDependencies__types_passport": "^1.0.9", + "npm_package_devDependencies__types_adm_zip": "^0.4.34", + "LOGNAME": "smallwhale", + "npm_lifecycle_script": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug --transpile-only -- src/server/index.ts", + "npm_config_color": "true", + "npm_package_dependencies_solr_node": "^1.2.1", + "npm_package_dependencies_react_transition_group": "^4.4.2", + "npm_package_dependencies_iink_js": "^1.5.4", + "npm_package_dependencies_html_webpack_plugin": "^5.5.0", + "npm_config_proxy": "", + "npm_config_package_lock": "true", + "npm_package_dependencies_prosemirror_state": "^1.4.1", + "npm_package_dependencies_nodemon": "^1.19.4", + "npm_package_dependencies_function_plot": "^1.22.8", + "npm_package_dependencies_equation_editor_react": "github:bobzel/equation-editor-react#useLocally", + "npm_package_devDependencies__types_socket_io_parser": "^3.0.0", + "VSCODE_GIT_IPC_HANDLE": "/var/folders/bv/1xck6d7j7bz14bvhgdsllbj40000gn/T/vscode-git-b8c8318074.sock", + "npm_config_package_lock_only": "", + "npm_config_fund": "true", + "npm_package_dependencies_react": "^18.2.0", + "npm_package_dependencies_bingmaps_react": "^1.2.10", + "npm_package_devDependencies_scss_loader": "0.0.1", + "npm_package_devDependencies__types_cookie_session": "^2.0.44", + "npm_config_save_optional": "", + "npm_package_dependencies_textarea_caret": "^3.1.0", + "npm_package_dependencies_react_measure": "^2.5.2", + "npm_package_dependencies_exif": "^0.6.0", + "NVM_BIN": "/Users/smallwhale/.nvm/versions/node/v12.16.0/bin", + "npm_config_ignore_scripts": "", + "npm_config_user_agent": "npm/6.13.4 node/v12.16.0 darwin x64", + "npm_package_dependencies_react_resizable": "^1.11.1", + "npm_package_dependencies_prosemirror_commands": "^1.2.1", + "npm_package_dependencies_memorystream": "^0.3.1", + "npm_package_dependencies_formidable": "1.2.1", + "npm_package_devDependencies__types_uuid": "^3.4.10", + "npm_config_cache_lock_wait": "10000", + "npm_package_dependencies_socket_io_client": "^2.5.0", + "npm_package_dependencies_recharts": "^2.1.12", + "npm_package_dependencies_react_chartjs_2": "^4.3.0", + "npm_package_dependencies_fluent_ffmpeg": "^2.1.2", + "npm_package_dependencies__types_cors": "^2.8.12", + "npm_package_devDependencies__types_node": "^10.17.60", + "npm_package_devDependencies__types_file_saver": "^2.0.5", + "VSCODE_GIT_ASKPASS_NODE": "/Applications/Visual Studio Code.app/Contents/Frameworks/Code Helper (Plugin).app/Contents/MacOS/Code Helper (Plugin)", + "GIT_ASKPASS": "/Applications/Visual Studio Code.app/Contents/Resources/app/extensions/git/dist/askpass.sh", + "HOMEBREW_CELLAR": "/opt/homebrew/Cellar", + "INFOPATH": "/opt/homebrew/share/info:/opt/homebrew/share/info:", + "npm_config_production": "", + "npm_package_dependencies_jsonschema": "^1.4.0", + "npm_package_dependencies_ffmpeg": "0.0.4", + "npm_package_dependencies_cookie_session": "^2.0.0", + "npm_package_dependencies_color": "^3.2.1", + "npm_package_devDependencies__types_webpack": "^4.41.32", + "npm_package_devDependencies__types_request_promise": "^4.1.48", + "npm_package_devDependencies__types_prosemirror_schema_list": "^1.0.3", + "npm_config_send_metrics": "", + "npm_config_save_bundle": "", + "npm_package_dependencies_web_request": "^1.0.7", + "npm_package_dependencies_react_datepicker": "^3.8.0", + "npm_package_dependencies_express": "^4.17.3", + "npm_package_dependencies_D": "^1.0.0", + "npm_package_dependencies__types_formidable": "1.0.31", + "npm_package_devDependencies__types_rc_switch": "^1.9.2", + "npm_package_devDependencies__types_prosemirror_dev_tools": "^2.1.0", + "npm_package_devDependencies__types_jquery": "^3.5.14", + "npm_config_umask": "0022", + "npm_config_node_options": "", + "npm_config_init_version": "1.0.0", + "npm_package_dependencies_https": "^1.0.0", + "npm_package_dependencies_array_batcher": "^1.2.3", + "npm_package_dependencies__fortawesome_free_regular_svg_icons": "^6.3.0", + "npm_package_devDependencies__types_shelljs": "^0.8.11", + "npm_package_devDependencies__types_libxmljs": "^0.18.7", + "npm_package_devDependencies__types_express_validator": "^3.0.0", + "npm_package_devDependencies__types_bluebird": "^3.5.36", + "npm_config_init_author_name": "", + "npm_config_git": "git", + "npm_config_scope": "", + "npm_package_dependencies_react_select": "^3.2.0", + "npm_package_dependencies_pdf_parse": "^1.1.1", + "npm_package_dependencies_colors": "^1.4.0", + "npm_package_dependencies_archiver": "^3.1.1", + "npm_package_devDependencies_css_loader": "^2.1.1", + "npm_package_devDependencies__types_socket_io_client": "^1.4.36", + "SECURITYSESSIONID": "186a4", + "npm_config_unsafe_perm": "true", + "npm_config_tmp": "/var/folders/bv/1xck6d7j7bz14bvhgdsllbj40000gn/T", + "npm_config_onload_script": "", + "npm_package_dependencies_serializr": "^1.5.4", + "npm_package_dependencies_fit_curve": "^0.1.7", + "npm_package_dependencies__webscopeio_react_textarea_autocomplete": "^4.9.1", + "npm_package_dependencies__types_three": "^0.126.2", + "npm_package_devDependencies_ts_node_dev": "^2.0.0", + "npm_node_execpath": "/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node", + "npm_config_prefix": "/Users/smallwhale/.nvm/versions/node/v12.16.0", + "npm_config_link": "", + "npm_config_format_package_lock": "true", + "npm_package_dependencies_passport": "^0.4.0", + "npm_package_devDependencies_eslint_plugin_react": "^7.30.1", + "npm_package_devDependencies__types_react_table": "^6.8.9", + "npm_package_devDependencies__types_react_reconciler": "^0.26.4", + "COLORTERM": "truecolor", + "NODE_OPTIONS": "--max_old_space_size=4096", + "TS_NODE_DEV": "true", + "_CLIENT_OPENAI_KEY": "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", + "_CLIENT_GITHUB_ACCESS_TOKEN": "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", + "VIPSHOME": "/usr/local/Cellar/vips/8.8.1", + "TYPESCRIPT_PATH": "/Users/smallwhale/Desktop/Projects/Dash-Web/node_modules/typescript/lib/typescript.js", + "TSCONFIG": "/Users/smallwhale/Desktop/Projects/Dash-Web/tsconfig.json", + "COMPILER_OPTIONS": "{}", + "TSLINT": "true", + "CONTEXT": "/Users/smallwhale/Desktop/Projects/Dash-Web", + "TSLINTAUTOFIX": "false", + "ESLINT": "false", + "ESLINT_OPTIONS": "{}", + "WATCH": "", + "WORK_DIVISION": "1", + "MEMORY_LIMIT": "2048", + "CHECK_SYNTACTIC_ERRORS": "false", + "USE_INCREMENTAL_API": "true", + "VUE": "false" + }, + "userLimits": { + "core_file_size_blocks": { + "soft": 0, + "hard": "unlimited" + }, + "data_seg_size_kbytes": { + "soft": "unlimited", + "hard": "unlimited" + }, + "file_size_blocks": { + "soft": "unlimited", + "hard": "unlimited" + }, + "max_locked_memory_bytes": { + "soft": "unlimited", + "hard": "unlimited" + }, + "max_memory_size_kbytes": { + "soft": "unlimited", + "hard": "unlimited" + }, + "open_files": { + "soft": 1048575, + "hard": "unlimited" + }, + "stack_size_bytes": { + "soft": 8372224, + "hard": 67092480 + }, + "cpu_time_seconds": { + "soft": "unlimited", + "hard": "unlimited" + }, + "max_user_processes": { + "soft": 1333, + "hard": 2000 + }, + "virtual_memory_kbytes": { + "soft": "unlimited", + "hard": "unlimited" + } + }, + "sharedObjects": [ + "/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node", + "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation", + "/usr/lib/libobjc.A.dylib", + "/System/Library/PrivateFrameworks/CoreServicesInternal.framework/Versions/A/CoreServicesInternal", + "/usr/lib/liboah.dylib", + "/usr/lib/libfakelink.dylib", + "/usr/lib/libicucore.A.dylib", + "/usr/lib/libSystem.B.dylib", + "/System/Library/PrivateFrameworks/SoftLinking.framework/Versions/A/SoftLinking", + "/usr/lib/libc++abi.dylib", + "/usr/lib/libc++.1.dylib", + "/usr/lib/system/libcache.dylib", + "/usr/lib/system/libcommonCrypto.dylib", + "/usr/lib/system/libcompiler_rt.dylib", + "/usr/lib/system/libcopyfile.dylib", + "/usr/lib/system/libcorecrypto.dylib", + "/usr/lib/system/libdispatch.dylib", + "/usr/lib/system/libdyld.dylib", + "/usr/lib/system/libkeymgr.dylib", + "/usr/lib/system/libmacho.dylib", + "/usr/lib/system/libquarantine.dylib", + "/usr/lib/system/libremovefile.dylib", + "/usr/lib/system/libsystem_asl.dylib", + "/usr/lib/system/libsystem_blocks.dylib", + "/usr/lib/system/libsystem_c.dylib", + "/usr/lib/system/libsystem_collections.dylib", + "/usr/lib/system/libsystem_configuration.dylib", + "/usr/lib/system/libsystem_containermanager.dylib", + "/usr/lib/system/libsystem_coreservices.dylib", + "/usr/lib/system/libsystem_darwin.dylib", + "/usr/lib/system/libsystem_dnssd.dylib", + "/usr/lib/system/libsystem_featureflags.dylib", + "/usr/lib/system/libsystem_info.dylib", + "/usr/lib/system/libsystem_m.dylib", + "/usr/lib/system/libsystem_malloc.dylib", + "/usr/lib/system/libsystem_networkextension.dylib", + "/usr/lib/system/libsystem_notify.dylib", + "/usr/lib/system/libsystem_sandbox.dylib", + "/usr/lib/system/libsystem_secinit.dylib", + "/usr/lib/system/libsystem_kernel.dylib", + "/usr/lib/system/libsystem_platform.dylib", + "/usr/lib/system/libsystem_pthread.dylib", + "/usr/lib/system/libsystem_symptoms.dylib", + "/usr/lib/system/libsystem_trace.dylib", + "/usr/lib/system/libunwind.dylib", + "/usr/lib/system/libxpc.dylib", + "/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit", + "/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices", + "/usr/lib/libDiagnosticMessagesClient.dylib", + "/usr/lib/libenergytrace.dylib", + "/usr/lib/libbsm.0.dylib", + "/usr/lib/libz.1.dylib", + "/usr/lib/system/libkxld.dylib", + "/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork", + "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/FSEvents", + "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore", + "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata", + "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices", + "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit", + "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE", + "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices", + "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices", + "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SharedFileList.framework/Versions/A/SharedFileList", + "/System/Library/Frameworks/Security.framework/Versions/A/Security", + "/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration", + "/usr/lib/libapple_nghttp2.dylib", + "/usr/lib/libcompression.dylib", + "/usr/lib/libnetwork.dylib", + "/usr/lib/libsqlite3.dylib", + "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation", + "/System/Library/Frameworks/Network.framework/Versions/A/Network", + "/usr/lib/libCoreEntitlements.dylib", + "/System/Library/PrivateFrameworks/MessageSecurity.framework/Versions/A/MessageSecurity", + "/System/Library/PrivateFrameworks/ProtocolBuffer.framework/Versions/A/ProtocolBuffer", + "/usr/lib/libMobileGestalt.dylib", + "/System/Library/PrivateFrameworks/AppleFSCompression.framework/Versions/A/AppleFSCompression", + "/usr/lib/libcoretls.dylib", + "/usr/lib/libcoretls_cfhelpers.dylib", + "/usr/lib/libpam.2.dylib", + "/usr/lib/libxar.1.dylib", + "/System/Library/PrivateFrameworks/CoreAutoLayout.framework/Versions/A/CoreAutoLayout", + "/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration", + "/usr/lib/libarchive.2.dylib", + "/usr/lib/libxml2.2.dylib", + "/usr/lib/liblangid.dylib", + "/System/Library/Frameworks/Combine.framework/Versions/A/Combine", + "/usr/lib/swift/libswiftCore.dylib", + "/usr/lib/swift/libswiftCoreFoundation.dylib", + "/usr/lib/swift/libswiftDarwin.dylib", + "/usr/lib/swift/libswiftDispatch.dylib", + "/usr/lib/swift/libswiftIOKit.dylib", + "/usr/lib/swift/libswiftObjectiveC.dylib", + "/usr/lib/swift/libswiftXPC.dylib", + "/usr/lib/swift/libswift_Concurrency.dylib", + "/usr/lib/swift/libswift_StringProcessing.dylib", + "/usr/lib/swift/libswiftos.dylib", + "/System/Library/PrivateFrameworks/AppleSystemInfo.framework/Versions/A/AppleSystemInfo", + "/System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/Versions/A/IOMobileFramebuffer", + "/System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface", + "/usr/lib/libpcap.A.dylib", + "/usr/lib/libdns_services.dylib", + "/usr/lib/liblzma.5.dylib", + "/usr/lib/libbz2.1.0.dylib", + "/usr/lib/libiconv.2.dylib", + "/usr/lib/libcharset.1.dylib", + "/usr/lib/swift/libswift_RegexParser.dylib", + "/usr/lib/libheimdal-asn1.dylib", + "/usr/lib/libCheckFix.dylib", + "/System/Library/PrivateFrameworks/TCC.framework/Versions/A/TCC", + "/System/Library/PrivateFrameworks/CoreNLP.framework/Versions/A/CoreNLP", + "/System/Library/PrivateFrameworks/MetadataUtilities.framework/Versions/A/MetadataUtilities", + "/System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate", + "/usr/lib/libmecab.dylib", + "/usr/lib/libCRFSuite.dylib", + "/usr/lib/libgermantok.dylib", + "/usr/lib/libThaiTokenizer.dylib", + "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/vImage", + "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/vecLib", + "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvMisc.dylib", + "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib", + "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib", + "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib", + "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib", + "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib", + "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib", + "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBNNS.dylib", + "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparse.dylib", + "/System/Library/PrivateFrameworks/MIL.framework/Versions/A/MIL", + "/System/Library/Frameworks/OpenDirectory.framework/Versions/A/Frameworks/CFOpenDirectory.framework/Versions/A/CFOpenDirectory", + "/System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory", + "/System/Library/PrivateFrameworks/APFS.framework/Versions/A/APFS", + "/System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation", + "/usr/lib/libutil.dylib", + "/System/Library/PrivateFrameworks/InstalledContentLibrary.framework/Versions/A/InstalledContentLibrary", + "/System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore", + "/usr/lib/libapp_launch_measurement.dylib", + "/System/Library/PrivateFrameworks/AppleMobileFileIntegrity.framework/Versions/A/AppleMobileFileIntegrity", + "/usr/lib/libmis.dylib", + "/System/Library/PrivateFrameworks/MobileSystemServices.framework/Versions/A/MobileSystemServices", + "/System/Library/PrivateFrameworks/ConfigProfileHelper.framework/Versions/A/ConfigProfileHelper", + "/System/Library/PrivateFrameworks/CoreAnalytics.framework/Versions/A/CoreAnalytics", + "/System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce", + "/System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling", + "/usr/lib/libxslt.1.dylib", + "/usr/lib/libcmph.dylib", + "/System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji", + "/System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData", + "/System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon", + "/System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement", + "/usr/lib/libTLE.dylib", + "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices", + "/System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics", + "/System/Library/Frameworks/CoreText.framework/Versions/A/CoreText", + "/System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO", + "/System/Library/Frameworks/ColorSync.framework/Versions/A/ColorSync", + "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/ATS", + "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices", + "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/PrintCore", + "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/QD", + "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy", + "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesis", + "/System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight", + "/System/Library/PrivateFrameworks/FontServices.framework/libFontParser.dylib", + "/System/Library/PrivateFrameworks/RunningBoardServices.framework/Versions/A/RunningBoardServices", + "/System/Library/PrivateFrameworks/IOSurfaceAccelerator.framework/Versions/A/IOSurfaceAccelerator", + "/System/Library/PrivateFrameworks/WatchdogClient.framework/Versions/A/WatchdogClient", + "/System/Library/Frameworks/CoreDisplay.framework/Versions/A/CoreDisplay", + "/System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia", + "/System/Library/PrivateFrameworks/IOAccelerator.framework/Versions/A/IOAccelerator", + "/System/Library/Frameworks/Metal.framework/Versions/A/Metal", + "/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo", + "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders", + "/System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/A/MultitouchSupport", + "/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore", + "/System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox", + "/System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard", + "/System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG", + "/usr/lib/libexpat.1.dylib", + "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libPng.dylib", + "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib", + "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libGIF.dylib", + "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJP2.dylib", + "/usr/lib/libate.dylib", + "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib", + "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib", + "/System/Library/PrivateFrameworks/GPUWrangler.framework/Versions/A/GPUWrangler", + "/System/Library/PrivateFrameworks/IOPresentment.framework/Versions/A/IOPresentment", + "/System/Library/PrivateFrameworks/DSExternalDisplay.framework/Versions/A/DSExternalDisplay", + "/System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/31001/Libraries/libllvm-flatbuffers.dylib", + "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib", + "/System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/31001/Libraries/libGPUCompilerUtils.dylib", + "/System/Library/PrivateFrameworks/CMCaptureCore.framework/Versions/A/CMCaptureCore", + "/usr/lib/libspindump.dylib", + "/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio", + "/System/Library/Frameworks/ExtensionFoundation.framework/Versions/A/ExtensionFoundation", + "/System/Library/PrivateFrameworks/CoreTime.framework/Versions/A/CoreTime", + "/System/Library/PrivateFrameworks/AppServerSupport.framework/Versions/A/AppServerSupport", + "/System/Library/PrivateFrameworks/perfdata.framework/Versions/A/perfdata", + "/System/Library/PrivateFrameworks/AudioToolboxCore.framework/Versions/A/AudioToolboxCore", + "/System/Library/PrivateFrameworks/caulk.framework/Versions/A/caulk", + "/usr/lib/libAudioStatistics.dylib", + "/System/Library/PrivateFrameworks/SystemPolicy.framework/Versions/A/SystemPolicy", + "/usr/lib/libSMC.dylib", + "/System/Library/Frameworks/CoreMIDI.framework/Versions/A/CoreMIDI", + "/usr/lib/libAudioToolboxUtility.dylib", + "/System/Library/PrivateFrameworks/OSAServicesClient.framework/Versions/A/OSAServicesClient", + "/usr/lib/libperfcheck.dylib", + "/System/Library/PrivateFrameworks/PlugInKit.framework/Versions/A/PlugInKit", + "/System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices", + "/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL", + "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib", + "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGFXShared.dylib", + "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib", + "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLImage.dylib", + "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCVMSPluginSupport.dylib", + "/usr/lib/libRosetta.dylib", + "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib", + "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSCore.framework/Versions/A/MPSCore", + "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSImage.framework/Versions/A/MPSImage", + "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNeuralNetwork.framework/Versions/A/MPSNeuralNetwork", + "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSMatrix.framework/Versions/A/MPSMatrix", + "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSRayIntersector.framework/Versions/A/MPSRayIntersector", + "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNDArray.framework/Versions/A/MPSNDArray", + "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSFunctions.framework/Versions/A/MPSFunctions", + "/System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools", + "/System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary", + "/usr/lib/libIOReport.dylib", + "/System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage", + "/System/Library/PrivateFrameworks/PhotosensitivityProcessing.framework/Versions/A/PhotosensitivityProcessing", + "/System/Library/Frameworks/OpenCL.framework/Versions/A/OpenCL", + "/System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer", + "/System/Library/PrivateFrameworks/FontServices.framework/Versions/A/FontServices", + "/System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers", + "/System/Library/PrivateFrameworks/OTSVG.framework/Versions/A/OTSVG", + "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib", + "/System/Library/PrivateFrameworks/FontServices.framework/libhvf.dylib", + "/System/Library/PrivateFrameworks/FontServices.framework/libXTFontStaticRegistryData.dylib", + "/System/Library/PrivateFrameworks/VideoToolboxParavirtualizationSupport.framework/Versions/A/VideoToolboxParavirtualizationSupport", + "/System/Library/PrivateFrameworks/AppleVA.framework/Versions/A/AppleVA", + "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATSUI.framework/Versions/A/ATSUI", + "/usr/lib/libcups.2.dylib", + "/System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos", + "/System/Library/Frameworks/GSS.framework/Versions/A/GSS", + "/usr/lib/libresolv.9.dylib", + "/System/Library/PrivateFrameworks/Heimdal.framework/Versions/A/Heimdal", + "/System/Library/Frameworks/Kerberos.framework/Versions/A/Libraries/libHeimdalProxy.dylib", + "/System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth", + "/System/Library/Frameworks/AVFAudio.framework/Versions/A/AVFAudio", + "/System/Library/PrivateFrameworks/AXCoreUtilities.framework/Versions/A/AXCoreUtilities", + "/System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox", + "/System/Library/PrivateFrameworks/AudioSession.framework/Versions/A/AudioSession", + "/System/Library/Frameworks/IOBluetooth.framework/Versions/A/IOBluetooth", + "/System/Library/PrivateFrameworks/MediaExperience.framework/Versions/A/MediaExperience", + "/System/Library/PrivateFrameworks/AudioSession.framework/libSessionUtility.dylib", + "/System/Library/PrivateFrameworks/AudioResourceArbitration.framework/Versions/A/AudioResourceArbitration", + "/System/Library/PrivateFrameworks/PowerLog.framework/Versions/A/PowerLog", + "/System/Library/Frameworks/CoreData.framework/Versions/A/CoreData", + "/System/Library/Frameworks/CoreBluetooth.framework/Versions/A/CoreBluetooth", + "/System/Library/Frameworks/AudioUnit.framework/Versions/A/AudioUnit", + "/System/Library/PrivateFrameworks/CoreUtils.framework/Versions/A/CoreUtils", + "/System/Library/PrivateFrameworks/CoreUtilsExtras.framework/Versions/A/CoreUtilsExtras", + "/System/Library/PrivateFrameworks/IO80211.framework/Versions/A/IO80211", + "/System/Library/PrivateFrameworks/MobileKeyBag.framework/Versions/A/MobileKeyBag", + "/System/Library/PrivateFrameworks/MallocStackLogging.framework/Versions/A/MallocStackLogging" + ] +} \ No newline at end of file diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 18222b32a..6b4106f56 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -14,7 +14,7 @@ type GPTCallOpts = { }; const callTypeMap: { [type: string]: GPTCallOpts } = { - summary: { model: 'text-davinci-003', maxTokens: 256, temp: 0.5, prompt: 'Summarize this text briefly: ' }, + summary: { model: 'text-davinci-003', maxTokens: 256, temp: 0.5, prompt: 'Summarize this text in simpler terms: ' }, edit: { model: 'text-davinci-003', maxTokens: 256, temp: 0.5, prompt: 'Reword this: ' }, completion: { model: 'text-davinci-003', maxTokens: 256, temp: 0.5, prompt: '' }, }; @@ -47,7 +47,7 @@ const gptAPICall = async (inputText: string, callType: GPTCallType) => { } }; -const gptImageCall = async (prompt: string) => { +const gptImageCall = async (prompt: string, n?: number) => { try { const configuration = new Configuration({ apiKey: process.env.OPENAI_KEY, @@ -55,33 +55,15 @@ const gptImageCall = async (prompt: string) => { const openai = new OpenAIApi(configuration); const response = await openai.createImage({ prompt: prompt, - n: 1, + n: n ?? 1, size: '1024x1024', }); - return response.data.data[0].url; + return response.data.data.map(data => data.url); + // return response.data.data[0].url; } catch (err) { console.error(err); return; } }; -// const gptEditCall = async (selectedText: string, fullText: string) => { -// try { -// const configuration = new Configuration({ -// apiKey: process.env.OPENAI_KEY, -// }); -// const openai = new OpenAIApi(configuration); -// const response = await openai.createCompletion({ -// model: 'text-davinci-003', -// max_tokens: 256, -// temperature: 0.1, -// prompt: `Replace the phrase ${selectedText} inside of ${fullText}.`, -// }); -// return response.data.choices[0].text.trim(); -// } catch (err) { -// console.log(err); -// return 'Error connecting with API.'; -// } -// }; - export { gptAPICall, gptImageCall, GPTCallType }; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 85101fcab..ac948c7c7 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -195,7 +195,7 @@ export namespace DragManager { } // drag a document and drop it (or make an embed/copy on drop) - export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, dropEvent?: () => any) { + export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, onDropCompleted?: (e?: DragCompleteEvent) => any) { const addAudioTag = (dropDoc: any) => { dropDoc && !dropDoc.author_date && (dropDoc.author_date = new DateField()); dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc); @@ -203,7 +203,7 @@ export namespace DragManager { }; const finishDrag = async (e: DragCompleteEvent) => { const docDragData = e.docDragData; - dropEvent?.(); // glr: optional additional function to be called - in this case with presentation trails + onDropCompleted?.(e); // glr: optional additional function to be called - in this case with presentation trails if (docDragData && !docDragData.droppedDocuments.length) { docDragData.dropAction = dragData.userDropAction || dragData.dropAction; docDragData.droppedDocuments = ( diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 67b7b39dd..1ff5688f9 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -137,7 +137,11 @@ export class TabDocView extends React.Component { setupMoveUpEvents( this, e, - e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), + e => + !e.defaultPrevented && + DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY, undefined, () => { + CollectionDockingView.CloseSplit(doc); + }), returnFalse, action(e => { if (this.view) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index db000d5de..747ab9249 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -238,11 +238,11 @@ export class CollectionFreeFormLinkView extends React.Component {} }; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 34a1229ba..5cdd6b5f2 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -354,6 +354,11 @@ export class WebBox extends ViewBoxAnnotatableComponent, dataDoc: Doc) => void; @@ -896,12 +897,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - console.log(this.dataDoc.text); - if (resIndex < this.gptRes.length) { - this.dataDoc.text = (this.dataDoc.text as RichTextField)?.Text + this.gptRes[resIndex]; + animateRes = (resIndex: number, newText: string) => { + if (resIndex < newText.length) { + const marks = this._editorView?.state.storedMarks ?? []; + // if (!marks) return; + this._editorView?.dispatch(this._editorView.state.tr.setStoredMarks(marks).insertText(newText[resIndex]).setStoredMarks(marks)); setTimeout(() => { - this.animateRes(resIndex + 1); + this.animateRes(resIndex + 1, newText); }, 20); } }; @@ -912,47 +914,68 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { + // const state = this._editorView?.state; + // if (!state) return; + // const to = state.selection.to; + // const updated = TextSelection.create(state.doc, to, to); + // this._editorView?.dispatch(state.tr.setSelection(updated).insertText('\n', to)); + // this._editorView?.dispatch(this._editorView.state.tr.setStoredMarks(marks).insertText('\nTesting').setStoredMarks(marks)); + // console.log('After ', this._editorView?.state.storedMarks); try { let res = await gptAPICall((this.dataDoc.text as RichTextField)?.Text, GPTCallType.COMPLETION); - if (res) { - this.gptRes = res; - this.animateRes(0); + if (!res) { + console.error('GPT call failed'); + this.animateRes(0, 'Something went wrong.'); + } else { + this.animateRes(0, res); } } catch (err) { - console.log(err); - this.dataDoc.text = (this.dataDoc.text as RichTextField)?.Text + 'Something went wrong'; + console.error('GPT call failed'); + this.animateRes(0, 'Something went wrong.'); } }); generateImage = async () => { console.log('Generate image from text: ', (this.dataDoc.text as RichTextField)?.Text); + GPTPopup.Instance?.setImgTargetDoc(this.rootDoc); + GPTPopup.Instance.setImgUrls([]); + GPTPopup.Instance.setMode(GPTPopupMode.IMAGE); + GPTPopup.Instance.setVisible(true); + GPTPopup.Instance.addToCollection = this.props.addDocument; + GPTPopup.Instance.setLoading(true); + try { - let image_url = await gptImageCall((this.dataDoc.text as RichTextField)?.Text); - if (image_url) { - const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_url] }); + // make this support multiple images + let image_urls = await gptImageCall((this.dataDoc.text as RichTextField)?.Text); + console.log(image_urls); + if (image_urls) { + const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_urls[0]] }); const source = Utils.prepend(result.accessPaths.agnostic.client); - const newDoc = Docs.Create.ImageDocument(source, { - x: NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) + 10, - y: NumCast(this.rootDoc.y), - _height: 200, - _width: 200, - data_nativeWidth: result.nativeWidth, - data_nativeHeight: result.nativeHeight, - }); - if (Doc.IsInMyOverlay(this.rootDoc)) { - newDoc.overlayX = this.rootDoc.x; - newDoc.overlayY = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height); - Doc.AddToMyOverlay(newDoc); - } else { - this.props.addDocument?.(newDoc); - } - // Create link between prompt and image - DocUtils.MakeLink(this.rootDoc, newDoc, { link_relationship: 'Image Prompt' }); + GPTPopup.Instance.setImgUrls([source]); + + // const newDoc = Docs.Create.ImageDocument(source, { + // x: NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) + 10, + // y: NumCast(this.rootDoc.y), + // _height: 200, + // _width: 200, + // data_nativeWidth: result.nativeWidth, + // data_nativeHeight: result.nativeHeight, + // }); + // if (Doc.IsInMyOverlay(this.rootDoc)) { + // newDoc.overlayX = this.rootDoc.x; + // newDoc.overlayY = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height); + // Doc.AddToMyOverlay(newDoc); + // } else { + // this.props.addDocument?.(newDoc); + // } + // // Create link between prompt and image + // DocUtils.MakeLink(this.rootDoc, newDoc, { link_relationship: 'Image Prompt' }); } } catch (err) { console.log(err); return ''; } + GPTPopup.Instance.setLoading(false); }; breakupDictation = () => { @@ -1249,8 +1272,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { gptSummarize = async (e: React.PointerEvent) => { GPTPopup.Instance.setVisible(true); this.setHighlightRange(undefined); - this.setGPTMode(GPTPopupMode.SUMMARY); + GPTPopup.Instance.setMode(GPTPopupMode.SUMMARY); GPTPopup.Instance.setLoading(true); try { @@ -183,6 +183,10 @@ export class AnchorMenu extends AntimodeMenu { GPTPopup.Instance.setLoading(false); }; + gptImage = async () => { + console.log(this.GetAnchor(undefined, false)); + }; + /** * Replaces text suggestions from GPT. */ @@ -293,21 +297,12 @@ export class AnchorMenu extends AntimodeMenu { )} - {/* */} {AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : ( } color={StrCast(Doc.UserDoc().userColor)} /> )} - {this.canEdit() && } color={StrCast(Doc.UserDoc().userColor)} />} + {/* Removed text editing for now, not quite ready */} + {/* {this.canEdit() && } color={StrCast(Doc.UserDoc().userColor)} />} */} + {} color={StrCast(Doc.UserDoc().userColor)} />} } popup={} color={StrCast(Doc.UserDoc().userColor)} /> {AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? null : ( } color={StrCast(Doc.UserDoc().userColor)} /> diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss index 2f0ff83e2..478b7d4ba 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.scss +++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss @@ -24,6 +24,7 @@ $highlightedText: #82e0ff; .summary-heading { display: flex; + justify-content: space-between; align-items: center; border-bottom: 1px solid $greyborder; padding-bottom: 5px; @@ -110,6 +111,57 @@ $highlightedText: #82e0ff; } } +.image-content-wrapper { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + padding-bottom: 16px; + + .img-wrapper { + position: relative; + cursor: pointer; + + .img-container { + position: relative; + + img { + position: relative; + } + } + + .img-container::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + opacity: 0; + transition: opacity 0.3s ease; + } + + .btn-container { + position: absolute; + right: 8px; + bottom: 8px; + opacity: 0; + transition: opacity 0.3s ease; + } + + &:hover { + .img-container::after { + opacity: 1; + } + + .btn-container { + opacity: 1; + } + } + } +} + // Typist CSS .Typist .Cursor { display: inline-block; diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index 9f28cb5d1..aeee90d16 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -1,4 +1,5 @@ import React = require('react'); +import './GPTPopup.scss'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, observable } from 'mobx'; import { observer } from 'mobx-react'; @@ -6,13 +7,14 @@ import ReactLoading from 'react-loading'; import Typist from 'react-typist'; import { Doc } from '../../../../fields/Doc'; import { DocUtils, Docs } from '../../../documents/Documents'; -import './GPTPopup.scss'; -import { Button, IconButton } from 'browndash-components'; -import { StrCast } from '../../../../fields/Types'; +import { Button, IconButton, Type } from 'browndash-components'; +import { NumCast, StrCast } from '../../../../fields/Types'; +import { CgClose } from 'react-icons/cg'; export enum GPTPopupMode { SUMMARY, EDIT, + IMAGE, } interface GPTPopupProps {} @@ -39,6 +41,14 @@ export class GPTPopup extends React.Component { public setText = (text: string) => { this.text = text; }; + + @observable + public imageUrls: string[] = []; + @action + public setImgUrls = (imgs: string[]) => { + this.imageUrls = imgs; + }; + @observable public mode: GPTPopupMode = GPTPopupMode.SUMMARY; @action @@ -58,6 +68,8 @@ export class GPTPopup extends React.Component { public setDone = (done: boolean) => { this.done = done; }; + + // change what can be a ref into a ref @observable private sidebarId: string = ''; @action @@ -65,19 +77,30 @@ export class GPTPopup extends React.Component { this.sidebarId = id; }; - // reference - // if (focusNode) { - // const anchor = srcWeb?.ComponentView?.getAnchor?.(true); - // anchor && DocUtils.MakeLink(htmlDoc, anchor, {}); - // } + // pdfs and webpages + @observable + private targetAnchor: Doc | undefined; + @action + public setTargetAnchor = (anchor: Doc) => { + this.targetAnchor = anchor; + }; + @observable - private pdfAnchor: Doc | undefined; + private imgTargetDoc: Doc | undefined; @action - public setPdfAnchor = (anchor: Doc) => { - this.pdfAnchor = anchor; + public setImgTargetDoc = (anchor: Doc) => { + this.imgTargetDoc = anchor; + }; + + @observable + private textAnchor: Doc | undefined; + @action + public setTextAnchor = (anchor: Doc) => { + this.textAnchor = anchor; }; public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false; + public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined; /** * Transfers the summarization text to a sidebar annotation text document. @@ -90,13 +113,42 @@ export class GPTPopup extends React.Component { _layout_autoHeight: true, }); this.addDoc(newDoc, this.sidebarId); - if (this.pdfAnchor) { - DocUtils.MakeLink(newDoc, this.pdfAnchor, { + if (this.targetAnchor) { + DocUtils.MakeLink(newDoc, this.targetAnchor, { link_relationship: 'GPT Summary', }); } }; + /** + * Transfers the image urls to actual image docs + */ + private transferToImage = (source: string) => { + console.log('Text Anchor', this.textAnchor); + console.log('Whole doc anchor', this.imgTargetDoc); + const textAnchor = this.textAnchor ?? this.imgTargetDoc; + if (!textAnchor) return; + const newDoc = Docs.Create.ImageDocument(source, { + x: NumCast(textAnchor.x) + NumCast(textAnchor._width) + 10, + y: NumCast(textAnchor.y), + _height: 200, + _width: 200, + data_nativeWidth: 1024, + data_nativeHeight: 1024, + }); + if (Doc.IsInMyOverlay(textAnchor)) { + newDoc.overlayX = textAnchor.x; + newDoc.overlayY = NumCast(textAnchor.y) + NumCast(textAnchor._height); + Doc.AddToMyOverlay(newDoc); + } else { + this.addToCollection?.(newDoc); + } + // Create link between prompt and image + DocUtils.MakeLink(textAnchor, newDoc, { link_relationship: 'Image Prompt' }); + }; + + private getPreviewUrl = (source: string) => source.split('.').join('_m.'); + constructor(props: GPTPopupProps) { super(props); GPTPopup.Instance = this; @@ -108,6 +160,26 @@ export class GPTPopup extends React.Component { } }; + imageBox = () => { + return ( +
+ {this.heading('GENERATED IMAGE')} +
+ {this.imageUrls.map(rawSrc => ( +
+
+ dalle generation +
+
+
+
+ ))} +
+
+ ); + }; + summaryBox = () => ( <>
@@ -135,30 +207,21 @@ export class GPTPopup extends React.Component {
{this.done ? ( <> - {/* - */} - } color={StrCast(Doc.UserDoc().userVariantColor)} /> - + }} + color={StrCast(Doc.UserDoc().userVariantColor)} + type={Type.TERT} + />
)}
@@ -216,14 +279,14 @@ export class GPTPopup extends React.Component { heading = (headingText: string) => (
- {this.loading && } + {this.loading ? : } onClick={() => this.setVisible(false)} />}
); render() { return (
- {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.editBox()} + {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : this.editBox()}
); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 4fc31ffe3..1d1c34f4f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -425,8 +425,7 @@ export class PDFViewer extends React.Component { GPTPopup.Instance.setSidebarId('data_sidebar'); const anchor = this._getAnchor(undefined, false); if (anchor) { - console.log(anchor); - GPTPopup.Instance.setPdfAnchor(anchor); + GPTPopup.Instance.setTargetAnchor(anchor); } GPTPopup.Instance.addDoc = this.props.sidebarAddDoc; }; -- cgit v1.2.3-70-g09d2 From 314f62bb5335e3fb3f25501823d41cf0cdc53cac Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Sun, 13 Aug 2023 17:02:58 -0400 Subject: update --- .../collectionFreeForm/CollectionFreeFormView.tsx | 18 +++- .../views/nodes/formattedText/FormattedTextBox.tsx | 15 +-- .../views/nodes/generativeFill/GenerativeFill.scss | 1 + .../views/nodes/generativeFill/GenerativeFill.tsx | 103 +++++++++--------- .../generativeFillUtils/BrushHandler.ts | 116 +++++++-------------- .../generativeFillUtils/ImageHandler.ts | 2 +- .../generativeFillUtils/generativeFillConstants.ts | 4 +- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 4 +- 8 files changed, 118 insertions(+), 145 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ba31916a7..34f707b30 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -237,7 +237,7 @@ export class CollectionFreeFormView extends CollectionSubView this.props.childClickScript || ScriptCast(this.Document.onChildClick); onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); elementFunc = () => this._layoutElements; - shrinkWrap = () => { + fitContentOnce = () => { if (this.props.DocumentView?.().nativeWidth) return; const vals = this.fitToContentVals; this.layoutDoc._freeform_panX = vals.bounds.cx; @@ -1592,6 +1592,14 @@ export class CollectionFreeFormView extends CollectionSubView (this._layoutElements = elements || []), { fireImmediately: true, name: 'doLayout' } ); + + this._disposers.fitContent = reaction( + () => this.rootDoc.fitContentOnce, + fitContentOnce => { + if (fitContentOnce) this.fitContentOnce(); + }, + { fireImmediately: true, name: 'fitContent' } + ); }) ); } @@ -1782,6 +1790,14 @@ export class CollectionFreeFormView extends CollectionSubView (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', + }); 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/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 9f4483e8d..072977150 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -937,6 +937,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { console.log('Generate image from text: ', (this.dataDoc.text as RichTextField)?.Text); + GPTPopup.Instance?.setTextAnchor(this.getAnchor(false)); GPTPopup.Instance?.setImgTargetDoc(this.rootDoc); GPTPopup.Instance.setImgUrls([]); GPTPopup.Instance.setMode(GPTPopupMode.IMAGE); @@ -1270,13 +1271,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent([0, 0]); // which urls were already saved to canvas - const savedSrcs = useRef([]); + const savedSrcs = useRef>(new Set()); // references to keep track of tree structure const newCollectionRef = useRef(null); const parentDoc = useRef(null); const childrenDocs = useRef([]); + const addToExistingCollection = useRef(false); // Undo and Redo const handleUndo = () => { @@ -250,18 +248,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD })); }; - // File upload - const uploadImg = (e: React.ChangeEvent) => { - if (e.target.files) { - const file = e.target.files[0]; - const image = new Image(); - const imgUrl = URL.createObjectURL(file); - image.src = imgUrl; - ImageUtility.drawImgToCanvas(image, canvasRef); - currImg.current = image; - } - }; - // Get AI Edit const getEdit = async () => { const img = currImg.current; @@ -280,6 +266,8 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD // create first image if (!newCollectionRef.current) { + if (addToExistingCollection.current) { + } if (!(originalImg.current && imageRootDoc)) return; console.log('creating first image'); // create new collection and add it to the view @@ -292,7 +280,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD }); DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History', link_displayLine: false }); // add the doc to the main freeform - addDoc?.(newCollectionRef.current); + // addDoc?.(newCollectionRef.current); await createNewImgDoc(originalImg.current, true); } else { parentDoc.current = childrenDocs.current[childrenDocs.current.length - 1]; @@ -321,7 +309,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD if (!parentDoc.current) return; const startY = NumCast(parentDoc.current.y); const len = childrenDocs.current.length; - console.log(len); let initialYPositions: number[] = []; for (let i = 0; i < len; i++) { initialYPositions.push(startY + i * offsetDistanceY); @@ -398,9 +385,10 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD }; const handleViewClose = () => { - // if (newCollectionRef.current) { - // CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right); - // } + if (newCollectionRef.current) { + newCollectionRef.current.fitContentOnce = true; + CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right); + } MainView.Instance.setImageEditorOpen(false); MainView.Instance.setImageEditorSource(''); setEdits([]); @@ -409,12 +397,17 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD return (
-

Generative Fill

+

AI Image Editor

+ {saveLoading && ( + + Saving image... + + )}
{/* Main canvas for editing */} @@ -439,20 +432,11 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
{/* Icons */}
- - { - if (fileRef.current) { - fileRef.current.click(); - } - }}> - - { setBrushStyle(BrushStyle.ADD); }}> - + {/* Undo and Redo */} + + { + setCursorData(prev => ({ ...prev, width: val as number })); + }} + /> +
{/* Edits thumbnails*/}
@@ -484,12 +490,14 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD width={100} height={100} src={edit} - onClick={() => { + onClick={async () => { + // if (savedSrcs.current.has(edit)) return; const img = new Image(); img.src = edit; ImageUtility.drawImgToCanvas(img, canvasRef); currImg.current = img; - onSave(); + savedSrcs.current.add(edit); + await onSave(); }} /> ))} @@ -521,14 +529,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD currImg.current = img; }} /> -
- {saveLoading && } -
)}
@@ -541,15 +541,14 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD type="text" label="Prompt" placeholder="Prompt..." - InputLabelProps={{ style: { fontSize: '1.5rem' } }} - inputProps={{ style: { fontSize: '1.5rem' } }} + InputLabelProps={{ style: { fontSize: '16px' } }} + inputProps={{ style: { fontSize: '16px' } }} sx={{ backgroundColor: '#ffffff', position: 'absolute', - bottom: '1rem', + bottom: '16px', transform: 'translateX(calc(50vw - 50%))', - width: 'calc(100vw - 4rem)', - scale: 1.2, + width: 'calc(100vw - 64px)', }} />
diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts index c2716e083..f84f04190 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts @@ -1,87 +1,45 @@ -import { GenerativeFillMathHelpers } from "./GenerativeFillMathHelpers"; -import { eraserColor } from "./generativeFillConstants"; -import { Point } from "./generativeFillInterfaces"; +import { GenerativeFillMathHelpers } from './GenerativeFillMathHelpers'; +import { eraserColor } from './generativeFillConstants'; +import { Point } from './generativeFillInterfaces'; export class BrushHandler { - static brushCircle = ( - x: number, - y: number, - brushRadius: number, - ctx: CanvasRenderingContext2D - ) => { - ctx.globalCompositeOperation = "destination-out"; - ctx.shadowColor = "#ffffffeb"; - ctx.shadowBlur = 5; - ctx.beginPath(); - ctx.arc(x, y, brushRadius, 0, 2 * Math.PI); - ctx.fill(); - ctx.closePath(); - }; + static brushCircle = (x: number, y: number, brushRadius: number, ctx: CanvasRenderingContext2D) => { + ctx.globalCompositeOperation = 'destination-out'; + ctx.shadowColor = '#ffffffeb'; + ctx.shadowBlur = 5; + ctx.beginPath(); + ctx.arc(x, y, brushRadius, 0, 2 * Math.PI); + ctx.fill(); + ctx.closePath(); + }; - static brushCircleOverlay = ( - x: number, - y: number, - brushRadius: number, - ctx: CanvasRenderingContext2D, - fillColor: string, - erase: boolean - ) => { - ctx.globalCompositeOperation = "destination-out"; - // ctx.globalCompositeOperation = erase ? "destination-out" : "source-over"; - ctx.fillStyle = fillColor; - ctx.shadowColor = eraserColor; - ctx.shadowBlur = 5; - ctx.beginPath(); - ctx.arc(x, y, brushRadius, 0, 2 * Math.PI); - ctx.fill(); - ctx.closePath(); - }; + static brushCircleOverlay = (x: number, y: number, brushRadius: number, ctx: CanvasRenderingContext2D, fillColor: string, erase: boolean) => { + ctx.globalCompositeOperation = 'destination-out'; + // ctx.globalCompositeOperation = erase ? "destination-out" : "source-over"; + ctx.fillStyle = fillColor; + ctx.shadowColor = eraserColor; + ctx.shadowBlur = 5; + ctx.beginPath(); + ctx.arc(x, y, brushRadius, 0, 2 * Math.PI); + ctx.fill(); + ctx.closePath(); + }; - static createBrushPath = ( - startPoint: Point, - endPoint: Point, - brushRadius: number, - ctx: CanvasRenderingContext2D - ) => { - const dist = GenerativeFillMathHelpers.distanceBetween( - startPoint, - endPoint - ); + static createBrushPath = (startPoint: Point, endPoint: Point, brushRadius: number, ctx: CanvasRenderingContext2D) => { + const dist = GenerativeFillMathHelpers.distanceBetween(startPoint, endPoint); - for (let i = 0; i < dist; i += 5) { - const s = i / dist; - BrushHandler.brushCircle( - startPoint.x * (1 - s) + endPoint.x * s, - startPoint.y * (1 - s) + endPoint.y * s, - brushRadius, - ctx - ); - } - }; + for (let i = 0; i < dist; i += 5) { + const s = i / dist; + BrushHandler.brushCircle(startPoint.x * (1 - s) + endPoint.x * s, startPoint.y * (1 - s) + endPoint.y * s, brushRadius, ctx); + } + }; - static createBrushPathOverlay = ( - startPoint: Point, - endPoint: Point, - brushRadius: number, - ctx: CanvasRenderingContext2D, - fillColor: string, - erase: boolean - ) => { - const dist = GenerativeFillMathHelpers.distanceBetween( - startPoint, - endPoint - ); + static createBrushPathOverlay = (startPoint: Point, endPoint: Point, brushRadius: number, ctx: CanvasRenderingContext2D, fillColor: string, erase: boolean) => { + const dist = GenerativeFillMathHelpers.distanceBetween(startPoint, endPoint); - for (let i = 0; i < dist; i += 5) { - const s = i / dist; - BrushHandler.brushCircleOverlay( - startPoint.x * (1 - s) + endPoint.x * s, - startPoint.y * (1 - s) + endPoint.y * s, - brushRadius, - ctx, - fillColor, - erase - ); - } - }; + for (let i = 0; i < dist; i += 5) { + const s = i / dist; + BrushHandler.brushCircleOverlay(startPoint.x * (1 - s) + endPoint.x * s, startPoint.y * (1 - s) + endPoint.y * s, brushRadius, ctx, fillColor, erase); + } + }; } diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts index 48055903c..45cf7196b 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts @@ -28,7 +28,7 @@ export class ImageUtility { fd.append('image', imgBlob, 'image.png'); fd.append('mask', maskBlob, 'mask.png'); fd.append('prompt', prompt); - fd.append('size', '1024x1024'); + fd.append('size', '512x512'); fd.append('n', n ? JSON.stringify(n) : '1'); fd.append('response_format', 'b64_json'); diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts index 5a8d33742..412a4d238 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts @@ -1,9 +1,9 @@ // constants -export const canvasSize = 1024; +export const canvasSize = 512; export const freeformRenderSize = 300; export const offsetDistanceY = freeformRenderSize + 200; export const offsetX = 200; -export const newCollectionSize = 1000; +export const newCollectionSize = 500; export const activeColor = '#1976d2'; export const eraserColor = '#e1e9ec'; diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index aeee90d16..fc6fc1af8 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -124,9 +124,7 @@ export class GPTPopup extends React.Component { * Transfers the image urls to actual image docs */ private transferToImage = (source: string) => { - console.log('Text Anchor', this.textAnchor); - console.log('Whole doc anchor', this.imgTargetDoc); - const textAnchor = this.textAnchor ?? this.imgTargetDoc; + const textAnchor = this.imgTargetDoc; if (!textAnchor) return; const newDoc = Docs.Create.ImageDocument(source, { x: NumCast(textAnchor.x) + NumCast(textAnchor._width) + 10, -- cgit v1.2.3-70-g09d2 From d3ecace5f03233e5d5ab2354c3f482418287ca9a Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Thu, 17 Aug 2023 16:14:53 -0400 Subject: broken version --- package-lock.json | 97 +++++++++++++++-- src/client/util/CurrentUserUtils.ts | 2 + src/client/views/MainView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 1 + src/client/views/global/globalScripts.ts | 8 +- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 28 +++++ src/client/views/nodes/ImageBox.tsx | 6 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 76 +++++++------- .../views/nodes/generativeFill/GenerativeFill.tsx | 115 +++++++++++++-------- .../nodes/generativeFill/GenerativeFillButtons.tsx | 6 +- src/client/views/pdf/AnchorMenu.tsx | 1 + src/client/views/pdf/GPTPopup/GPTPopup.scss | 2 + src/client/views/pdf/GPTPopup/GPTPopup.tsx | 99 +++++++++--------- src/client/views/pdf/PDFViewer.tsx | 4 - 14 files changed, 301 insertions(+), 146 deletions(-) (limited to 'src') diff --git a/package-lock.json b/package-lock.json index 658e783f9..20d582e5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6507,9 +6507,9 @@ } }, "browndash-components": { - "version": "0.0.92", - "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.92.tgz", - "integrity": "sha512-eE/6WQNZiLnaXUKyoaMm0PDYjExUsFJ9VTAIIxROpYPosIBKWNZ743xaOfmehib5us9hEXJb0CvUFJQb8rzDVw==", + "version": "0.0.93", + "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.93.tgz", + "integrity": "sha512-JL9Hi7Nq+zHZtjLCQSuGhID0Hd7X6sARutyJqbECfsLWACsIC/JfeEQZdUeH5CqO+R7pJKeoWM82AIkWGtN2Xw==", "requires": { "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", @@ -7063,12 +7063,97 @@ "strip-ansi": "^7.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "strip-ansi": { "version": "7.1.0", "bundled": true, "requires": { "ansi-regex": "^6.0.1" } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + } + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } } } }, @@ -8573,7 +8658,7 @@ } }, "string-width-cjs": { - "version": "npm:string-width@4.2.3", + "version": "npm:string-width-cjs@4.2.3", "bundled": true, "requires": { "emoji-regex": "^8.0.0", @@ -8596,7 +8681,7 @@ } }, "strip-ansi-cjs": { - "version": "npm:strip-ansi@6.0.1", + "version": "npm:strip-ansi-cjs@6.0.1", "bundled": true, "requires": { "ansi-regex": "^5.0.1" @@ -8755,7 +8840,7 @@ } }, "wrap-ansi-cjs": { - "version": "npm:wrap-ansi@7.0.0", + "version": "npm:wrap-ansi-cjs@7.0.0", "bundled": true, "requires": { "ansi-styles": "^4.0.0", diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index c9a5175bb..6592ecffc 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -633,6 +633,8 @@ export class CurrentUserUtils { { title: "Snap", icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"snaplines", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform { title: "Grid", icon: "border-all", toolTip: "Show Grid", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"grid", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform { title: "View All", icon: "object-group", toolTip: "Fit all Docs to View", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform + // want the same style as toggle button, but don't want it to act as an actual toggle, so set disableToggle to true, + { title: "Fit All", icon: "arrows-left-right", toolTip: "Fit all Docs to View (persistent)", btnType: ButtonType.IconClickButton, ignoreClick: false, expertMode: false, toolType:"viewAllPersist", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform { title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"clusters", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform { title: "Cards", icon: "brain", toolTip: "Flashcards", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"flashcards", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform { title: "Arrange",icon:"arrow-down-short-wide",toolTip:"Toggle Auto Arrange", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"arrange", funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 60eb64caa..8fba9b886 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -80,7 +80,6 @@ export class MainView extends React.Component { @observable public imageEditorSource: string = ''; @action public setImageEditorSource = (source: string) => (this.imageEditorSource = source); @observable public imageRootDoc: Doc | undefined; - @action public setImageRootDoc = (doc: Doc) => (this.imageRootDoc = doc); @observable public addDoc: ((doc: Doc | Doc[], annotationKey?: string) => boolean) | undefined; @observable public LastButton: Opt; @@ -350,6 +349,7 @@ export class MainView extends React.Component { fa.faMousePointer, fa.faMusic, fa.faObjectGroup, + fa.faArrowsLeftRight, fa.faPause, fa.faPen, fa.faPenNib, diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 29122cb91..f353dfab7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1599,6 +1599,7 @@ export class CollectionFreeFormView extends CollectionSubView this.rootDoc.fitContentOnce, fitContentOnce => { if (fitContentOnce) this.fitContentOnce(); + this.rootDoc.fitContentOnce = false; }, { fireImmediately: true, name: 'fitContent' } ); diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index 256377758..38bf1042d 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -85,10 +85,10 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log('[FontIconBox.tsx] toggleOverlay failed'); }); -ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) { +ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'viewAllPersist', checkResult?: boolean) { const selected = SelectionManager.Docs().lastElement(); // prettier-ignore - const map: Map<'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([ + const map: Map<'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll' | 'viewAllPersist', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([ ['grid', { checkResult: (doc:Doc) => BoolCast(doc._freeform_backgroundGrid, false), setDoc: (doc:Doc) => doc._freeform_backgroundGrid = !doc._freeform_backgroundGrid, @@ -101,6 +101,10 @@ ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snapli checkResult: (doc:Doc) => BoolCast(doc._freeform_fitContentsToBox, false), setDoc: (doc:Doc) => doc._freeform_fitContentsToBox = !doc._freeform_fitContentsToBox, }], + ['viewAllPersist', { + checkResult: (doc:Doc) => false, + setDoc: (doc:Doc) => doc.fitContentOnce = true + }], ['clusters', { waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire checkResult: (doc:Doc) => BoolCast(doc._freeform_useClusters, false), diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 91eac675f..90ec212fc 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -29,6 +29,7 @@ export enum ButtonType { DropdownList = 'dropdownList', DropdownButton = 'dropdownBtn', ClickButton = 'clickBtn', + IconClickButton = 'iconClickBtn', ToggleButton = 'toggleBtn', ColorButton = 'colorBtn', ToolButton = 'toolBtn', @@ -346,6 +347,32 @@ export class FontIconBox extends DocComponent() { ); } + @computed get iconClickButton() { + // Determine the type of toggle button + const buttonText: string = StrCast(this.rootDoc.buttonText); + const tooltip: string = StrCast(this.rootDoc.toolTip); + + const script = ScriptCast(this.rootDoc.onClick); + const toggleStatus = script ? script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: undefined, _readOnly_: true }).result : false; + // Colors + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + + return ( + script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: !toggleStatus, _readOnly_: false })} + /> + ); + } + /** * Default */ @@ -393,6 +420,7 @@ export class FontIconBox extends DocComponent() { case ButtonType.DropdownButton: return this.dropdownButton; case ButtonType.MultiToggleButton: return this.multiToggleButton; case ButtonType.ToggleButton: return this.toggleButton; + case ButtonType.IconClickButton: return this.iconClickButton; case ButtonType.ClickButton: case ButtonType.ToolButton: return ; case ButtonType.TextButton: return
))} + {!this.loading && ( + <> + } color={StrCast(Doc.UserDoc().userVariantColor)} /> + + )} ); }; @@ -227,43 +269,6 @@ export class GPTPopup extends React.Component { ); - editBox = () => { - const hr = this.highlightRange; - return ( - <> -
- {this.heading('TEXT EDIT SUGGESTIONS')} -
- {hr && ( -
- {this.text.slice(0, hr[0])} {this.text.slice(hr[0], hr[1])} {this.text.slice(hr[1])} -
- )} -
-
- {hr && !this.loading && ( - <> -
- <> - - - -
- {this.aiWarning()} - - )} - - ); - }; - aiWarning = () => this.done ? (
@@ -277,14 +282,14 @@ export class GPTPopup extends React.Component { heading = (headingText: string) => (
- {this.loading ? : } onClick={() => this.setVisible(false)} />} + {this.loading ? : } onClick={() => this.setVisible(false)} />}
); render() { return (
- {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : this.editBox()} + {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : <>}
); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index c9fee4813..1319a236d 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -424,10 +424,6 @@ export class PDFViewer extends React.Component { // Changing which document to add the annotation to (the currently selected PDF) GPTPopup.Instance.setSidebarId('data_sidebar'); - const anchor = this._getAnchor(undefined, false); - if (anchor) { - GPTPopup.Instance.setTargetAnchor(anchor); - } GPTPopup.Instance.addDoc = this.props.sidebarAddDoc; }; -- cgit v1.2.3-70-g09d2 From 592e1a1b1000157db77bd812b8debfbcc45844f9 Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Thu, 17 Aug 2023 17:52:12 -0400 Subject: revert --- src/client/views/nodes/ImageBox.tsx | 4 +- .../views/nodes/generativeFill/GenerativeFill.tsx | 115 ++++++++------------- .../generativeFillUtils/ImageHandler.ts | 2 + 3 files changed, 46 insertions(+), 75 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 352e0fbdc..603994016 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -252,8 +252,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent { - MainView.Instance.imageEditorOpen = true; - MainView.Instance.imageEditorSource = this.choosePath(field.url); + MainView.Instance.setImageEditorOpen(true); + MainView.Instance.setImageEditorSource(this.choosePath(field.url)); MainView.Instance.addDoc = this.props.addDocument; MainView.Instance.imageRootDoc = this.rootDoc; }), diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index 99068d647..f136982bc 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -3,7 +3,7 @@ import React = require('react'); import { useEffect, useRef, useState } from 'react'; import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler'; import { BrushHandler } from './generativeFillUtils/BrushHandler'; -import { Box, Checkbox, FormControlLabel, IconButton, Slider, TextField } from '@mui/material'; +import { Box, IconButton, Slider, TextField } from '@mui/material'; import { CursorData, Point } from './generativeFillUtils/generativeFillInterfaces'; import { activeColor, canvasSize, eraserColor, freeformRenderSize, newCollectionSize, offsetDistanceY, offsetX } from './generativeFillUtils/generativeFillConstants'; import { PointerHandler } from './generativeFillUtils/PointerHandler'; @@ -19,7 +19,6 @@ import { Cast, DocCast, NumCast } from '../../../../fields/Types'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; import { OpenWhere, OpenWhereMod } from '../DocumentView'; import { Oval } from 'react-loader-spinner'; -import { CheckBox } from '../../search/CheckBox'; /** * For images not 1024x1024 fill in the rest in solid black, or a @@ -73,7 +72,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD const [input, setInput] = useState(''); const [loading, setLoading] = useState(false); const [saveLoading, setSaveLoading] = useState(false); - const [isNewCollection, setIsNewCollection] = useState(false); // the current image in the main canvas const currImg = useRef(null); // the unedited version of each generation (parent) @@ -83,6 +81,9 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD // stores redo stack const redoStack = useRef([]); + // early stage properly, likely will get rid of + const freeformPosition = useRef([0, 0]); + // which urls were already saved to canvas const savedSrcs = useRef>(new Set()); @@ -90,6 +91,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD const newCollectionRef = useRef(null); const parentDoc = useRef(null); const childrenDocs = useRef([]); + const addToExistingCollection = useRef(false); // Undo and Redo const handleUndo = () => { @@ -109,13 +111,10 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD }; const handleRedo = () => { - const ctx = ImageUtility.getCanvasContext(canvasRef); - if (!ctx || !currImg.current || !canvasRef.current) return; - + // TODO: handle undo as well const target = redoStack.current[redoStack.current.length - 1]; if (!target) { } else { - undoStack.current = [...undoStack.current, canvasRef.current?.toDataURL()]; const img = new Image(); img.src = target; ImageUtility.drawImgToCanvas(img, canvasRef); @@ -185,10 +184,13 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD console.log('first load'); if (!imageEditorSource || imageEditorSource === '') return; const img = new Image(); + console.log('source', imageEditorSource); img.src = imageEditorSource; + console.log('drawing image'); ImageUtility.drawImgToCanvas(img, canvasRef); currImg.current = img; originalImg.current = img; + freeformPosition.current = [0, 0]; return () => { console.log('cleanup'); @@ -197,6 +199,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD childrenDocs.current = []; currImg.current = null; originalImg.current = null; + freeformPosition.current = [0, 0]; undoStack.current = []; redoStack.current = []; }; @@ -265,25 +268,24 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD // create first image if (!newCollectionRef.current) { - if (!isNewCollection) { - // newcollection should stay null - } else { - if (!(originalImg.current && imageRootDoc)) return; - // create new collection and add it to the view - newCollectionRef.current = Docs.Create.FreeformDocument([], { - x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + offsetX, - y: NumCast(imageRootDoc.y), - _width: newCollectionSize, - _height: newCollectionSize, - title: 'Image edit collection', - }); - DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History', link_displayLine: false }); - // add the doc to the main freeform - addDoc?.(newCollectionRef.current); - await createNewImgDoc(originalImg.current, true); + if (addToExistingCollection.current) { } + if (!(originalImg.current && imageRootDoc)) return; + console.log('creating first image'); + // create new collection and add it to the view + newCollectionRef.current = Docs.Create.FreeformDocument([], { + x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + offsetX, + y: NumCast(imageRootDoc.y), + _width: newCollectionSize, + _height: newCollectionSize, + title: 'Image edit collection', + }); + DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History', link_displayLine: false }); + // add the doc to the main freeform + // addDoc?.(newCollectionRef.current); + await createNewImgDoc(originalImg.current, true); } else { - // parentDoc.current = childrenDocs.current[childrenDocs.current.length - 1]; + parentDoc.current = childrenDocs.current[childrenDocs.current.length - 1]; childrenDocs.current = []; } @@ -292,24 +294,12 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD const { urls } = res as APISuccess; const image = new Image(); image.src = urls[0]; - - // need to crop images agh - // save to dash - - // const imageRes = await Promise.all( - // urls.map(async url => { - // const newImg = new Image(); - // newImg.src = url; - // await onSave(newImg); - // }) - // ); - - // map each url to [url, imgDoc] setEdits(urls); - ImageUtility.drawImgToCanvas(image, canvasRef); currImg.current = image; - // onSave(currImg.current); + onSave(); + freeformPosition.current[0] += 1; + freeformPosition.current[1] = 0; } catch (err) { console.log(err); } @@ -336,7 +326,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD // creates a new image document and returns its reference const createNewImgDoc = async (img: HTMLImageElement, firstDoc: boolean): Promise => { - if (!imageRootDoc) return; + if (!newCollectionRef.current || !imageRootDoc) return; const src = img.src; const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [src] }); const source = Utils.prepend(result.accessPaths.agnostic.client); @@ -344,6 +334,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD if (firstDoc) { const x = 0; const initialY = 0; + console.log('first doc'); const newImg = Docs.Create.ImageDocument(source, { x: x, @@ -354,19 +345,16 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD data_nativeHeight: result.nativeHeight, }); - if (isNewCollection && newCollectionRef.current) { - Doc.AddDocToList(newCollectionRef.current, undefined, newImg); - } else { - addDoc?.(newImg); - } - + Doc.AddDocToList(newCollectionRef.current, undefined, newImg); parentDoc.current = newImg; return newImg; } else { if (!parentDoc.current) return; const x = NumCast(parentDoc.current.x) + freeformRenderSize + offsetX; // dummy position + console.log('creating child elements'); const initialY = 0; + const newImg = Docs.Create.ImageDocument(source, { x: x, y: initialY, @@ -377,26 +365,21 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD }); childrenDocs.current.push(newImg); - - if (isNewCollection && newCollectionRef.current) { - Doc.AddDocToList(newCollectionRef.current, undefined, newImg); - } else { - addDoc?.(newImg); - } - DocUtils.MakeLink(parentDoc.current, newImg, { link_relationship: 'Image Edit', link_displayLine: true }); adjustImgPositions(); + + Doc.AddDocToList(newCollectionRef.current, undefined, newImg); return newImg; } }; // need to maybe call on every img click, not just when the save btn is clicked - const onSave = async (img: HTMLImageElement) => { + const onSave = async () => { setSaveLoading(true); - // if (!currImg.current || !originalImg.current || !imageRootDoc) return; + if (!currImg.current || !originalImg.current || !imageRootDoc) return; try { console.log('creating another image'); - await createNewImgDoc(img, false); + await createNewImgDoc(currImg.current, false); } catch (err) { console.log(err); } @@ -406,7 +389,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD const handleViewClose = () => { if (newCollectionRef.current) { newCollectionRef.current.fitContentOnce = true; - // CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right); + CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right); } MainView.Instance.setImageEditorOpen(false); MainView.Instance.setImageEditorSource(''); @@ -418,19 +401,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD

AI Image Editor

- { - setIsNewCollection(prev => !prev); - }} - /> - } - label={'Create New Collection'} - labelPlacement="end" - sx={{ whiteSpace: 'nowrap' }} - /> @@ -523,14 +493,13 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD height={100} src={edit} onClick={async () => { + // if (savedSrcs.current.has(edit)) return; const img = new Image(); img.src = edit; ImageUtility.drawImgToCanvas(img, canvasRef); currImg.current = img; savedSrcs.current.add(edit); - undoStack.current = []; - redoStack.current = []; - await onSave(img); + await onSave(); }} /> ))} diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts index 45cf7196b..1954ab3fb 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts @@ -101,7 +101,9 @@ export class ImageUtility { ctx.clearRect(0, 0, canvasSize, canvasSize); ctx.drawImage(img, 0, 0, width, height); } else { + console.log('loading image'); img.onload = () => { + console.log('loaded'); const ctx = this.getCanvasContext(canvasRef); if (!ctx) return; ctx.globalCompositeOperation = 'source-over'; -- cgit v1.2.3-70-g09d2 From 107f718199a5b0e15f62e5c7e7034b11047ff512 Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Thu, 17 Aug 2023 19:11:42 -0400 Subject: baseline working version, different image sizes work --- .../views/nodes/generativeFill/GenerativeFill.tsx | 93 +++++++++------- .../generativeFillUtils/ImageHandler.ts | 119 +++++++++++++++++---- .../generativeFillUtils/generativeFillConstants.ts | 3 +- 3 files changed, 156 insertions(+), 59 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index f136982bc..4d475149d 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -42,10 +42,9 @@ enum BrushStyle { MARQUEE, } -interface ImageEdit { - imgElement: HTMLImageElement; - parent: ImageEdit; - children: ImageEdit[]; +interface ImageDimensions { + width: number; + height: number; } interface GenerativeFillProps { @@ -59,7 +58,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD const canvasRef = useRef(null); const canvasBackgroundRef = useRef(null); const drawingAreaRef = useRef(null); - const fileRef = useRef(null); const [cursorData, setCursorData] = useState({ x: 0, y: 0, @@ -72,6 +70,10 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD const [input, setInput] = useState(''); const [loading, setLoading] = useState(false); const [saveLoading, setSaveLoading] = useState(false); + const [canvasDims, setCanvasDims] = useState({ + width: canvasSize, + height: canvasSize, + }); // the current image in the main canvas const currImg = useRef(null); // the unedited version of each generation (parent) @@ -91,7 +93,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD const newCollectionRef = useRef(null); const parentDoc = useRef(null); const childrenDocs = useRef([]); - const addToExistingCollection = useRef(false); // Undo and Redo const handleUndo = () => { @@ -100,12 +101,12 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD const target = undoStack.current[undoStack.current.length - 1]; if (!target) { - ImageUtility.drawImgToCanvas(currImg.current, canvasRef); + ImageUtility.drawImgToCanvas(currImg.current, canvasRef, canvasDims.width, canvasDims.height); } else { redoStack.current = [...redoStack.current, canvasRef.current.toDataURL()]; const img = new Image(); img.src = target; - ImageUtility.drawImgToCanvas(img, canvasRef); + ImageUtility.drawImgToCanvas(img, canvasRef, canvasDims.width, canvasDims.height); undoStack.current = undoStack.current.slice(0, -1); } }; @@ -117,7 +118,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD } else { const img = new Image(); img.src = target; - ImageUtility.drawImgToCanvas(img, canvasRef); + ImageUtility.drawImgToCanvas(img, canvasRef, canvasDims.width, canvasDims.height); redoStack.current = redoStack.current.slice(0, -1); } }; @@ -129,7 +130,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD ctx.clearRect(0, 0, canvasSize, canvasSize); undoStack.current = []; redoStack.current = []; - ImageUtility.drawImgToCanvas(currImg.current, canvasRef, true); + ImageUtility.drawImgToCanvas(currImg.current, canvasRef, canvasSize, canvasSize); }; // initiate brushing @@ -144,7 +145,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD setIsBrushing(true); const { x, y } = PointerHandler.getPointRelativeToElement(canvas, e, canvasScale); - BrushHandler.brushCircleOverlay(x, y, cursorData.width / 2 / canvasScale, ctx, eraserColor, brushStyle === BrushStyle.SUBTRACT); }; @@ -181,19 +181,21 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD // first load useEffect(() => { - console.log('first load'); if (!imageEditorSource || imageEditorSource === '') return; const img = new Image(); - console.log('source', imageEditorSource); img.src = imageEditorSource; - console.log('drawing image'); - ImageUtility.drawImgToCanvas(img, canvasRef); currImg.current = img; originalImg.current = img; - freeformPosition.current = [0, 0]; + img.onload = () => { + const imgWidth = img.naturalWidth; + const imgHeight = img.naturalHeight; + const scale = Math.min(canvasSize / imgWidth, canvasSize / imgHeight); + const width = imgWidth * scale; + const height = imgHeight * scale; + setCanvasDims({ width, height }); + }; return () => { - console.log('cleanup'); newCollectionRef.current = null; parentDoc.current = null; childrenDocs.current = []; @@ -202,9 +204,15 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD freeformPosition.current = [0, 0]; undoStack.current = []; redoStack.current = []; + ImageUtility.clearCanvas(canvasRef); }; }, [canvasRef, imageEditorSource]); + useEffect(() => { + if (!currImg.current) return; + ImageUtility.drawImgToCanvas(currImg.current, canvasRef, canvasDims.width, canvasDims.height); + }, [canvasDims]); + // handles brush sizing useEffect(() => { const handleKeyPress = (e: KeyboardEvent) => { @@ -261,15 +269,18 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD setLoading(true); // need to adjust later try { - const maskBlob = await ImageUtility.canvasToBlob(canvas); - const imgBlob = await ImageUtility.canvasToBlob(ImageUtility.getCanvasImg(img)); + const canvasOriginalImg = ImageUtility.getCanvasImg(img); + if (!canvasOriginalImg) return; + const canvasMask = ImageUtility.getCanvasMask(canvas); + if (!canvasMask) return; + // ImageUtility.downloadCanvas(canvasMask); + const maskBlob = await ImageUtility.canvasToBlob(canvasMask); + const imgBlob = await ImageUtility.canvasToBlob(canvasOriginalImg); const res = await ImageUtility.getEdit(imgBlob, maskBlob, input !== '' ? input + ' in the same style' : 'Fill in the image in the same style', 2); // const res = await ImageUtility.mockGetEdit(img.src); // create first image if (!newCollectionRef.current) { - if (addToExistingCollection.current) { - } if (!(originalImg.current && imageRootDoc)) return; console.log('creating first image'); // create new collection and add it to the view @@ -281,25 +292,30 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD title: 'Image edit collection', }); DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History', link_displayLine: false }); + + // opening new tab + newCollectionRef.current.fitContentOnce = true; + CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right); + // add the doc to the main freeform // addDoc?.(newCollectionRef.current); await createNewImgDoc(originalImg.current, true); } else { - parentDoc.current = childrenDocs.current[childrenDocs.current.length - 1]; + // parentDoc.current = childrenDocs.current[childrenDocs.current.length - 1]; childrenDocs.current = []; } originalImg.current = currImg.current; const { urls } = res as APISuccess; + const imgUrls = await Promise.all(urls.map(url => ImageUtility.convertImageToCanvasDataURL(url, canvasDims.width, canvasDims.height))); const image = new Image(); - image.src = urls[0]; - setEdits(urls); - ImageUtility.drawImgToCanvas(image, canvasRef); + image.src = imgUrls[0]; + + setEdits(imgUrls); + ImageUtility.drawImgToCanvas(image, canvasRef, canvasDims.width, canvasDims.height); currImg.current = image; onSave(); - freeformPosition.current[0] += 1; - freeformPosition.current[1] = 0; } catch (err) { console.log(err); } @@ -334,7 +350,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD if (firstDoc) { const x = 0; const initialY = 0; - console.log('first doc'); const newImg = Docs.Create.ImageDocument(source, { x: x, @@ -387,10 +402,10 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD }; const handleViewClose = () => { - if (newCollectionRef.current) { - newCollectionRef.current.fitContentOnce = true; - CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right); - } + // if (newCollectionRef.current) { + // newCollectionRef.current.fitContentOnce = true; + // CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right); + // } MainView.Instance.setImageEditorOpen(false); MainView.Instance.setImageEditorSource(''); setEdits([]); @@ -420,8 +435,8 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD onPointerMove={updateCursorData} onPointerDown={handlePointerDown} onPointerUp={handlePointerUp}> - - + +
( { // if (savedSrcs.current.has(edit)) return; const img = new Image(); img.src = edit; - ImageUtility.drawImgToCanvas(img, canvasRef); + ImageUtility.drawImgToCanvas(img, canvasRef, canvasDims.width, canvasDims.height); currImg.current = img; savedSrcs.current.add(edit); await onSave(); @@ -519,15 +533,14 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD Original { if (!originalImg.current) return; const img = new Image(); img.src = originalImg.current.src; - ImageUtility.drawImgToCanvas(img, canvasRef); + ImageUtility.drawImgToCanvas(img, canvasRef, canvasDims.width, canvasDims.height); currImg.current = img; }} /> diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts index 1954ab3fb..8d32221bd 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts @@ -1,5 +1,5 @@ import { RefObject } from 'react'; -import { canvasSize } from './generativeFillConstants'; +import { bgColor, canvasSize } from './generativeFillConstants'; export interface APISuccess { status: 'success'; @@ -22,6 +22,47 @@ export class ImageUtility { }); }; + // given a square api image, get the cropped img + static getCroppedImg = (img: HTMLImageElement, width: number, height: number): HTMLCanvasElement | undefined => { + // Create a new canvas element + const canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + const ctx = canvas.getContext('2d'); + if (ctx) { + // Clear the canvas + ctx.clearRect(0, 0, canvas.width, canvas.height); + if (width < height) { + // horizontal padding, x offset + const xOffset = (canvasSize - width) / 2; + console.log(xOffset); + ctx.drawImage(img, xOffset, 0, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height); + } else { + // vertical padding, y offset + const yOffset = (canvasSize - height) / 2; + ctx.drawImage(img, 0, yOffset, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height); + } + return canvas; + } + }; + + static convertImageToCanvasDataURL = async (imageSrc: string, width: number, height: number): Promise => { + return new Promise((resolve, reject) => { + const img = new Image(); + img.onload = () => { + const canvas = this.getCroppedImg(img, width, height); + if (canvas) { + const dataUrl = canvas.toDataURL(); + resolve(dataUrl); + } + }; + img.onerror = error => { + reject(error); + }; + img.src = imageSrc; + }); + }; + static getEdit = async (imgBlob: Blob, maskBlob: Blob, prompt: string, n?: number): Promise => { const apiUrl = 'https://api.openai.com/v1/images/edits'; const fd = new FormData(); @@ -90,41 +131,83 @@ export class ImageUtility { }; }; - static drawImgToCanvas = (img: HTMLImageElement, canvasRef: React.RefObject, loaded?: boolean) => { - if (loaded) { + static clearCanvas = (canvasRef: React.RefObject) => { + const ctx = this.getCanvasContext(canvasRef); + if (!ctx || !canvasRef.current) return; + ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height); + }; + + static drawImgToCanvas = (img: HTMLImageElement, canvasRef: React.RefObject, width: number, height: number) => { + const drawImg = (img: HTMLImageElement) => { const ctx = this.getCanvasContext(canvasRef); if (!ctx) return; ctx.globalCompositeOperation = 'source-over'; - const scale = Math.max(canvasSize / img.width, canvasSize / img.height); - const width = img.width * scale; - const height = img.height * scale; - ctx.clearRect(0, 0, canvasSize, canvasSize); + ctx.clearRect(0, 0, width, height); ctx.drawImage(img, 0, 0, width, height); + }; + + if (img.complete) { + drawImg(img); } else { console.log('loading image'); img.onload = () => { - console.log('loaded'); - const ctx = this.getCanvasContext(canvasRef); - if (!ctx) return; - ctx.globalCompositeOperation = 'source-over'; - const scale = Math.max(canvasSize / img.width, canvasSize / img.height); - const width = img.width * scale; - const height = img.height * scale; - ctx.clearRect(0, 0, canvasSize, canvasSize); - ctx.drawImage(img, 0, 0, width, height); + drawImg(img); }; } }; // The image must be loaded! - static getCanvasImg = (img: HTMLImageElement): HTMLCanvasElement => { + static getCanvasMask = (srcCanvas: HTMLCanvasElement): HTMLCanvasElement | undefined => { const canvas = document.createElement('canvas'); canvas.width = canvasSize; canvas.height = canvasSize; const ctx = canvas.getContext('2d'); + if (!ctx) return; ctx?.clearRect(0, 0, canvasSize, canvasSize); - ctx?.drawImage(img, 0, 0, canvasSize, canvasSize); + ctx.fillStyle = bgColor; + ctx.fillRect(0, 0, canvasSize, canvasSize); + // extract and set padding data + if (srcCanvas.height > srcCanvas.width) { + // horizontal padding, x offset + const xOffset = (canvasSize - srcCanvas.width) / 2; + ctx?.clearRect(xOffset, 0, srcCanvas.width, srcCanvas.height); + ctx.drawImage(srcCanvas, xOffset, 0, srcCanvas.width, srcCanvas.height); + } else { + // vertical padding, y offset + const yOffset = (canvasSize - srcCanvas.height) / 2; + ctx?.clearRect(0, yOffset, srcCanvas.width, srcCanvas.height); + ctx.drawImage(srcCanvas, 0, yOffset, srcCanvas.width, srcCanvas.height); + } + return canvas; + }; + + // The image must be loaded! + static getCanvasImg = (img: HTMLImageElement): HTMLCanvasElement | undefined => { + const canvas = document.createElement('canvas'); + canvas.width = canvasSize; + canvas.height = canvasSize; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + // fix scaling + const scale = Math.min(canvasSize / img.width, canvasSize / img.height); + const width = img.width * scale; + const height = img.height * scale; + ctx?.clearRect(0, 0, canvasSize, canvasSize); + ctx.fillStyle = bgColor; + ctx.fillRect(0, 0, canvasSize, canvasSize); + + // extract and set padding data + if (img.naturalHeight > img.naturalWidth) { + // horizontal padding, x offset + const xOffset = (canvasSize - width) / 2; + + ctx.drawImage(img, xOffset, 0, width, height); + } else { + // vertical padding, y offset + const yOffset = (canvasSize - height) / 2; + ctx.drawImage(img, 0, yOffset, width, height); + } return canvas; }; } diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts index 412a4d238..286dc6e4c 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts @@ -1,9 +1,10 @@ // constants export const canvasSize = 512; export const freeformRenderSize = 300; -export const offsetDistanceY = freeformRenderSize + 200; +export const offsetDistanceY = freeformRenderSize + 300; export const offsetX = 200; export const newCollectionSize = 500; export const activeColor = '#1976d2'; export const eraserColor = '#e1e9ec'; +export const bgColor = '#f0f4f6'; -- cgit v1.2.3-70-g09d2 From 1f44bed14529f8bfc280f714ffd794813c1b7a73 Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Thu, 17 Aug 2023 21:02:33 -0400 Subject: baseline before changes --- src/client/apis/gpt/GPT.ts | 1 - .../views/nodes/generativeFill/GenerativeFill.tsx | 61 ++++++---------------- .../generativeFillUtils/ImageHandler.ts | 10 ++-- .../generativeFillUtils/generativeFillConstants.ts | 2 +- .../generativeFillInterfaces.ts | 19 ++++--- 5 files changed, 32 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 6b4106f56..6bde7989b 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -39,7 +39,6 @@ const gptAPICall = async (inputText: string, callType: GPTCallType) => { temperature: opts.temp, prompt: `${opts.prompt}${inputText}`, }); - // console.log(response.data.choices[0]); return response.data.choices[0].text; } catch (err) { console.log(err); diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index 4d475149d..97d1cbf20 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -4,37 +4,21 @@ import { useEffect, useRef, useState } from 'react'; import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler'; import { BrushHandler } from './generativeFillUtils/BrushHandler'; import { Box, IconButton, Slider, TextField } from '@mui/material'; -import { CursorData, Point } from './generativeFillUtils/generativeFillInterfaces'; +import { CursorData, ImageDimensions, Point } from './generativeFillUtils/generativeFillInterfaces'; import { activeColor, canvasSize, eraserColor, freeformRenderSize, newCollectionSize, offsetDistanceY, offsetX } from './generativeFillUtils/generativeFillConstants'; import { PointerHandler } from './generativeFillUtils/PointerHandler'; import { BsEraser, BsX } from 'react-icons/bs'; import { CiUndo, CiRedo } from 'react-icons/ci'; -import Buttons from './GenerativeFillButtons'; import { MainView } from '../../MainView'; import { Doc } from '../../../../fields/Doc'; import { Networking } from '../../../Network'; import { Utils } from '../../../../Utils'; import { DocUtils, Docs } from '../../../documents/Documents'; -import { Cast, DocCast, NumCast } from '../../../../fields/Types'; +import { NumCast } from '../../../../fields/Types'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; -import { OpenWhere, OpenWhereMod } from '../DocumentView'; +import { OpenWhereMod } from '../DocumentView'; import { Oval } from 'react-loader-spinner'; - -/** - * For images not 1024x1024 fill in the rest in solid black, or a - * reflected version of the image. - * - * add a branch from image directly checkbox - */ - -/** - * - * - * CollectionDockingView.AddSplit(Doc.MakeCopy(DocCast(Doc.UserDoc().emptyPane)), OpenWhereMod.right); - * CollectionDockingView.AddSplit(newCollection,OpenWhere.inParent) - * mind mapping - * this.props.addDocTab(); - */ +import Buttons from './GenerativeFillButtons'; enum BrushStyle { ADD, @@ -42,11 +26,6 @@ enum BrushStyle { MARQUEE, } -interface ImageDimensions { - width: number; - height: number; -} - interface GenerativeFillProps { imageEditorOpen: boolean; imageEditorSource: string; @@ -112,10 +91,13 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD }; const handleRedo = () => { - // TODO: handle undo as well + const ctx = ImageUtility.getCanvasContext(canvasRef); + if (!ctx || !currImg.current || !canvasRef.current) return; + const target = redoStack.current[redoStack.current.length - 1]; if (!target) { } else { + undoStack.current = [...undoStack.current, canvasRef.current?.toDataURL()]; const img = new Image(); img.src = target; ImageUtility.drawImgToCanvas(img, canvasRef, canvasDims.width, canvasDims.height); @@ -123,6 +105,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD } }; + // resets any erase strokes const handleReset = () => { if (!canvasRef.current || !currImg.current) return; const ctx = ImageUtility.getCanvasContext(canvasRef); @@ -130,7 +113,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD ctx.clearRect(0, 0, canvasSize, canvasSize); undoStack.current = []; redoStack.current = []; - ImageUtility.drawImgToCanvas(currImg.current, canvasRef, canvasSize, canvasSize); + ImageUtility.drawImgToCanvas(currImg.current, canvasRef, canvasDims.width, canvasDims.height); }; // initiate brushing @@ -236,7 +219,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD e.preventDefault(); e.stopPropagation(); const delta = e.deltaY; - const scaleFactor = delta > 0 ? 0.98 : 1.02; // Adjust the scale factor as per your requirement + const scaleFactor = delta > 0 ? 0.98 : 1.02; setCanvasScale(prevScale => prevScale * scaleFactor); }; @@ -267,13 +250,11 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD const ctx = ImageUtility.getCanvasContext(canvasRef); if (!ctx) return; setLoading(true); - // need to adjust later try { const canvasOriginalImg = ImageUtility.getCanvasImg(img); if (!canvasOriginalImg) return; const canvasMask = ImageUtility.getCanvasMask(canvas); if (!canvasMask) return; - // ImageUtility.downloadCanvas(canvasMask); const maskBlob = await ImageUtility.canvasToBlob(canvasMask); const imgBlob = await ImageUtility.canvasToBlob(canvasOriginalImg); const res = await ImageUtility.getEdit(imgBlob, maskBlob, input !== '' ? input + ' in the same style' : 'Fill in the image in the same style', 2); @@ -282,7 +263,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD // create first image if (!newCollectionRef.current) { if (!(originalImg.current && imageRootDoc)) return; - console.log('creating first image'); // create new collection and add it to the view newCollectionRef.current = Docs.Create.FreeformDocument([], { x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + offsetX, @@ -294,19 +274,17 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History', link_displayLine: false }); // opening new tab - newCollectionRef.current.fitContentOnce = true; CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right); + newCollectionRef.current.fitContentOnce = true; // add the doc to the main freeform - // addDoc?.(newCollectionRef.current); await createNewImgDoc(originalImg.current, true); } else { - // parentDoc.current = childrenDocs.current[childrenDocs.current.length - 1]; + parentDoc.current = childrenDocs.current[childrenDocs.current.length - 1]; childrenDocs.current = []; } originalImg.current = currImg.current; - const { urls } = res as APISuccess; const imgUrls = await Promise.all(urls.map(url => ImageUtility.convertImageToCanvasDataURL(url, canvasDims.width, canvasDims.height))); const image = new Image(); @@ -366,8 +344,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD } else { if (!parentDoc.current) return; const x = NumCast(parentDoc.current.x) + freeformRenderSize + offsetX; - // dummy position - console.log('creating child elements'); const initialY = 0; const newImg = Docs.Create.ImageDocument(source, { @@ -388,12 +364,10 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD } }; - // need to maybe call on every img click, not just when the save btn is clicked const onSave = async () => { setSaveLoading(true); if (!currImg.current || !originalImg.current || !imageRootDoc) return; try { - console.log('creating another image'); await createNewImgDoc(currImg.current, false); } catch (err) { console.log(err); @@ -402,10 +376,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD }; const handleViewClose = () => { - // if (newCollectionRef.current) { - // newCollectionRef.current.fitContentOnce = true; - // CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right); - // } MainView.Instance.setImageEditorOpen(false); MainView.Instance.setImageEditorSource(''); setEdits([]); @@ -435,8 +405,8 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD onPointerMove={updateCursorData} onPointerDown={handlePointerDown} onPointerUp={handlePointerUp}> - - + +
{ - // if (savedSrcs.current.has(edit)) return; const img = new Image(); img.src = edit; ImageUtility.drawImgToCanvas(img, canvasRef, canvasDims.width, canvasDims.height); diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts index 8d32221bd..4847bfeed 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts @@ -35,7 +35,6 @@ export class ImageUtility { if (width < height) { // horizontal padding, x offset const xOffset = (canvasSize - width) / 2; - console.log(xOffset); ctx.drawImage(img, xOffset, 0, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height); } else { // vertical padding, y offset @@ -156,7 +155,7 @@ export class ImageUtility { } }; - // The image must be loaded! + // The image must be loaded static getCanvasMask = (srcCanvas: HTMLCanvasElement): HTMLCanvasElement | undefined => { const canvas = document.createElement('canvas'); canvas.width = canvasSize; @@ -164,7 +163,7 @@ export class ImageUtility { const ctx = canvas.getContext('2d'); if (!ctx) return; ctx?.clearRect(0, 0, canvasSize, canvasSize); - ctx.fillStyle = bgColor; + ctx.fillStyle = '#000000'; ctx.fillRect(0, 0, canvasSize, canvasSize); // extract and set padding data @@ -182,7 +181,7 @@ export class ImageUtility { return canvas; }; - // The image must be loaded! + // The image must be loaded static getCanvasImg = (img: HTMLImageElement): HTMLCanvasElement | undefined => { const canvas = document.createElement('canvas'); canvas.width = canvasSize; @@ -194,10 +193,9 @@ export class ImageUtility { const width = img.width * scale; const height = img.height * scale; ctx?.clearRect(0, 0, canvasSize, canvasSize); - ctx.fillStyle = bgColor; + ctx.fillStyle = '#000000'; ctx.fillRect(0, 0, canvasSize, canvasSize); - // extract and set padding data if (img.naturalHeight > img.naturalWidth) { // horizontal padding, x offset const xOffset = (canvasSize - width) / 2; diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts index 286dc6e4c..1fe151b46 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts @@ -1,7 +1,7 @@ // constants export const canvasSize = 512; export const freeformRenderSize = 300; -export const offsetDistanceY = freeformRenderSize + 300; +export const offsetDistanceY = freeformRenderSize + 400; export const offsetX = 200; export const newCollectionSize = 500; diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillInterfaces.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillInterfaces.ts index 9b9b9d3c2..83a21a1a5 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillInterfaces.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillInterfaces.ts @@ -1,16 +1,21 @@ // interfaces export interface CursorData { - x: number; - y: number; - width: number; + x: number; + y: number; + width: number; } export interface Point { - x: number; - y: number; + x: number; + y: number; } export enum BrushMode { - ADD, - SUBTRACT, + ADD, + SUBTRACT, +} + +export interface ImageDimensions { + width: number; + height: number; } -- cgit v1.2.3-70-g09d2 From bce4589c0bd871437c83bf44a6a818d01b9bf0f2 Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Fri, 18 Aug 2023 16:52:12 -0400 Subject: reflected image padding --- .../views/nodes/generativeFill/GenerativeFill.tsx | 141 +++++++++++++-------- .../nodes/generativeFill/GenerativeFillButtons.tsx | 11 +- .../generativeFillUtils/ImageHandler.ts | 88 +++++++++++-- 3 files changed, 163 insertions(+), 77 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index 97d1cbf20..92ce3a8a8 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -3,22 +3,23 @@ import React = require('react'); import { useEffect, useRef, useState } from 'react'; import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler'; import { BrushHandler } from './generativeFillUtils/BrushHandler'; -import { Box, IconButton, Slider, TextField } from '@mui/material'; +import { Box, Checkbox, FormControlLabel, IconButton, Slider, TextField } from '@mui/material'; import { CursorData, ImageDimensions, Point } from './generativeFillUtils/generativeFillInterfaces'; import { activeColor, canvasSize, eraserColor, freeformRenderSize, newCollectionSize, offsetDistanceY, offsetX } from './generativeFillUtils/generativeFillConstants'; import { PointerHandler } from './generativeFillUtils/PointerHandler'; import { BsEraser, BsX } from 'react-icons/bs'; import { CiUndo, CiRedo } from 'react-icons/ci'; import { MainView } from '../../MainView'; -import { Doc } from '../../../../fields/Doc'; +import { Doc, DocListCast } from '../../../../fields/Doc'; import { Networking } from '../../../Network'; -import { Utils } from '../../../../Utils'; +import { Utils, returnEmptyDoclist } from '../../../../Utils'; import { DocUtils, Docs } from '../../../documents/Documents'; -import { NumCast } from '../../../../fields/Types'; +import { DocCast, NumCast } from '../../../../fields/Types'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; import { OpenWhereMod } from '../DocumentView'; import { Oval } from 'react-loader-spinner'; import Buttons from './GenerativeFillButtons'; +import { List } from '../../../../fields/List'; enum BrushStyle { ADD, @@ -33,6 +34,8 @@ interface GenerativeFillProps { addDoc: ((doc: Doc | Doc[], annotationKey?: string) => boolean) | undefined; } +// New field on image doc: gen_fill_children => list of children Docs + const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc }: GenerativeFillProps) => { const canvasRef = useRef(null); const canvasBackgroundRef = useRef(null); @@ -44,15 +47,18 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD }); const [isBrushing, setIsBrushing] = useState(false); const [canvasScale, setCanvasScale] = useState(0.5); - const [edits, setEdits] = useState([]); + // format: array of [image source, corresponding image Doc] + const [edits, setEdits] = useState<(string | Doc)[][]>([]); + const [edited, setEdited] = useState(false); const [brushStyle, setBrushStyle] = useState(BrushStyle.ADD); const [input, setInput] = useState(''); const [loading, setLoading] = useState(false); - const [saveLoading, setSaveLoading] = useState(false); const [canvasDims, setCanvasDims] = useState({ width: canvasSize, height: canvasSize, }); + // whether to create a new collection or not + const [isNewCollection, setIsNewCollection] = useState(true); // the current image in the main canvas const currImg = useRef(null); // the unedited version of each generation (parent) @@ -62,12 +68,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD // stores redo stack const redoStack = useRef([]); - // early stage properly, likely will get rid of - const freeformPosition = useRef([0, 0]); - - // which urls were already saved to canvas - const savedSrcs = useRef>(new Set()); - // references to keep track of tree structure const newCollectionRef = useRef(null); const parentDoc = useRef(null); @@ -178,13 +178,14 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD setCanvasDims({ width, height }); }; + // cleanup return () => { + setEdited(false); newCollectionRef.current = null; parentDoc.current = null; childrenDocs.current = []; currImg.current = null; originalImg.current = null; - freeformPosition.current = [0, 0]; undoStack.current = []; redoStack.current = []; ImageUtility.clearCanvas(canvasRef); @@ -250,10 +251,11 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD const ctx = ImageUtility.getCanvasContext(canvasRef); if (!ctx) return; setLoading(true); + setEdited(true); try { const canvasOriginalImg = ImageUtility.getCanvasImg(img); if (!canvasOriginalImg) return; - const canvasMask = ImageUtility.getCanvasMask(canvas); + const canvasMask = ImageUtility.getCanvasMask(canvas, canvasOriginalImg); if (!canvasMask) return; const maskBlob = await ImageUtility.canvasToBlob(canvasMask); const imgBlob = await ImageUtility.canvasToBlob(canvasOriginalImg); @@ -262,38 +264,46 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD // create first image if (!newCollectionRef.current) { - if (!(originalImg.current && imageRootDoc)) return; - // create new collection and add it to the view - newCollectionRef.current = Docs.Create.FreeformDocument([], { - x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + offsetX, - y: NumCast(imageRootDoc.y), - _width: newCollectionSize, - _height: newCollectionSize, - title: 'Image edit collection', - }); - DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History', link_displayLine: false }); - - // opening new tab - CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right); - newCollectionRef.current.fitContentOnce = true; - - // add the doc to the main freeform - await createNewImgDoc(originalImg.current, true); + if (!isNewCollection && imageRootDoc) { + // new collection stays null + parentDoc.current = imageRootDoc; + } else { + if (!(originalImg.current && imageRootDoc)) return; + // create new collection and add it to the view + newCollectionRef.current = Docs.Create.FreeformDocument([], { + x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + offsetX, + y: NumCast(imageRootDoc.y), + _width: newCollectionSize, + _height: newCollectionSize, + title: 'Image edit collection', + }); + DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History', link_displayLine: false }); + + // opening new tab + CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right); + + // add the doc to the main freeform + await createNewImgDoc(originalImg.current, true); + } } else { - parentDoc.current = childrenDocs.current[childrenDocs.current.length - 1]; childrenDocs.current = []; } originalImg.current = currImg.current; const { urls } = res as APISuccess; - const imgUrls = await Promise.all(urls.map(url => ImageUtility.convertImageToCanvasDataURL(url, canvasDims.width, canvasDims.height))); + const imgUrls = await Promise.all(urls.map(url => ImageUtility.convertImgToCanvasUrl(url, canvasDims.width, canvasDims.height))); + const imgRes = await Promise.all( + imgUrls.map(async url => { + const saveRes = await onSave(url); + return [url, saveRes as Doc]; + }) + ); + setEdits(imgRes); const image = new Image(); image.src = imgUrls[0]; - setEdits(imgUrls); ImageUtility.drawImgToCanvas(image, canvasRef, canvasDims.width, canvasDims.height); currImg.current = image; - onSave(); } catch (err) { console.log(err); } @@ -320,7 +330,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD // creates a new image document and returns its reference const createNewImgDoc = async (img: HTMLImageElement, firstDoc: boolean): Promise => { - if (!newCollectionRef.current || !imageRootDoc) return; + if (!imageRootDoc) return; const src = img.src; const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [src] }); const source = Utils.prepend(result.accessPaths.agnostic.client); @@ -337,8 +347,12 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD data_nativeWidth: result.nativeWidth, data_nativeHeight: result.nativeHeight, }); - - Doc.AddDocToList(newCollectionRef.current, undefined, newImg); + // add a new doc list field to newimg + if (isNewCollection && newCollectionRef.current) { + Doc.AddDocToList(newCollectionRef.current, undefined, newImg); + } else { + addDoc?.(newImg); + } parentDoc.current = newImg; return newImg; } else { @@ -354,30 +368,40 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD data_nativeWidth: result.nativeWidth, data_nativeHeight: result.nativeHeight, }); - + newImg.gen_fill_children = new List([]); childrenDocs.current.push(newImg); - DocUtils.MakeLink(parentDoc.current, newImg, { link_relationship: 'Image Edit', link_displayLine: true }); + // DocListCast(parentDoc.current.gen_fill_children).push(newImg); + DocUtils.MakeLink(parentDoc.current, newImg, { link_relationship: `Image edit; Prompt: ${input}`, link_displayLine: true }); adjustImgPositions(); - Doc.AddDocToList(newCollectionRef.current, undefined, newImg); + if (isNewCollection && newCollectionRef.current) { + Doc.AddDocToList(newCollectionRef.current, undefined, newImg); + } else { + addDoc?.(newImg); + } return newImg; } }; - const onSave = async () => { - setSaveLoading(true); + const onSave = async (src: string) => { + const img = new Image(); + img.src = src; if (!currImg.current || !originalImg.current || !imageRootDoc) return; try { - await createNewImgDoc(currImg.current, false); + const res = await createNewImgDoc(img, false); + return res; } catch (err) { console.log(err); } - setSaveLoading(false); }; const handleViewClose = () => { MainView.Instance.setImageEditorOpen(false); MainView.Instance.setImageEditorSource(''); + if (newCollectionRef.current) { + newCollectionRef.current.fitContentOnce = true; + } + setEdits([]); }; @@ -386,15 +410,25 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD

AI Image Editor

+ { + setIsNewCollection(prev => !prev); + }} + /> + } + label={'Create New Collection'} + labelPlacement="end" + sx={{ whiteSpace: 'nowrap' }} + /> - {saveLoading && ( - - Saving image... - - )}
{/* Main canvas for editing */} @@ -475,14 +509,13 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD { const img = new Image(); - img.src = edit; + img.src = edit[0] as string; ImageUtility.drawImgToCanvas(img, canvasRef, canvasDims.width, canvasDims.height); currImg.current = img; - savedSrcs.current.add(edit); - await onSave(); + parentDoc.current = edit[1] as Doc; }} /> ))} diff --git a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx index b4d56b408..e15af0a56 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx @@ -14,19 +14,10 @@ interface ButtonContainerProps { onReset: () => void; } -const Buttons = ({ canvasRef, currImg, loading, getEdit, onReset }: ButtonContainerProps) => { - +const Buttons = ({ loading, getEdit, onReset }: ButtonContainerProps) => { return (
- {/* */} - {/* */} - +
); }; diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts index ace7ebcae..f4ec70fbc 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts @@ -3,16 +3,6 @@ import { eraserColor } from './generativeFillConstants'; import { Point } from './generativeFillInterfaces'; export class BrushHandler { - static brushCircle = (x: number, y: number, brushRadius: number, ctx: CanvasRenderingContext2D) => { - ctx.globalCompositeOperation = 'destination-out'; - ctx.shadowColor = '#ffffffeb'; - ctx.shadowBlur = 5; - ctx.beginPath(); - ctx.arc(x, y, brushRadius, 0, 2 * Math.PI); - ctx.fill(); - ctx.closePath(); - }; - static brushCircleOverlay = (x: number, y: number, brushRadius: number, ctx: CanvasRenderingContext2D, fillColor: string, erase: boolean) => { ctx.globalCompositeOperation = 'destination-out'; ctx.fillStyle = fillColor; @@ -24,15 +14,6 @@ export class BrushHandler { ctx.closePath(); }; - static createBrushPath = (startPoint: Point, endPoint: Point, brushRadius: number, ctx: CanvasRenderingContext2D) => { - const dist = GenerativeFillMathHelpers.distanceBetween(startPoint, endPoint); - - for (let i = 0; i < dist; i += 5) { - const s = i / dist; - BrushHandler.brushCircle(startPoint.x * (1 - s) + endPoint.x * s, startPoint.y * (1 - s) + endPoint.y * s, brushRadius, ctx); - } - }; - static createBrushPathOverlay = (startPoint: Point, endPoint: Point, brushRadius: number, ctx: CanvasRenderingContext2D, fillColor: string, erase: boolean) => { const dist = GenerativeFillMathHelpers.distanceBetween(startPoint, endPoint); diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/GenerativeFillMathHelpers.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/GenerativeFillMathHelpers.ts index 027b99a52..97e03ff20 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/GenerativeFillMathHelpers.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/GenerativeFillMathHelpers.ts @@ -1,11 +1,10 @@ -import { Point } from "./generativeFillInterfaces"; +import { Point } from './generativeFillInterfaces'; export class GenerativeFillMathHelpers { - // math helpers - static distanceBetween = (p1: Point, p2: Point) => { - return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2)); - }; - static angleBetween = (p1: Point, p2: Point) => { - return Math.atan2(p2.x - p1.x, p2.y - p1.y); - }; + static distanceBetween = (p1: Point, p2: Point) => { + return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2)); + }; + static angleBetween = (p1: Point, p2: Point) => { + return Math.atan2(p2.x - p1.x, p2.y - p1.y); + }; } diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts index 4ff70c86c..c6ecedb6b 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts @@ -12,6 +12,11 @@ export interface APIError { } export class ImageUtility { + /** + * + * @param canvas Canvas to convert + * @returns Blob of canvas + */ static canvasToBlob = (canvas: HTMLCanvasElement): Promise => { return new Promise(resolve => { canvas.toBlob(blob => { @@ -45,6 +50,7 @@ export class ImageUtility { } }; + // converts an image to a canvas data url static convertImgToCanvasUrl = async (imageSrc: string, width: number, height: number): Promise => { return new Promise((resolve, reject) => { const img = new Image(); @@ -62,6 +68,7 @@ export class ImageUtility { }); }; + // calls the openai api to get image edits static getEdit = async (imgBlob: Blob, maskBlob: Blob, prompt: string, n?: number): Promise => { const apiUrl = 'https://api.openai.com/v1/images/edits'; const fd = new FormData(); @@ -92,6 +99,7 @@ export class ImageUtility { } }; + // mock api call static mockGetEdit = async (mockSrc: string): Promise => { return { status: 'success', @@ -99,6 +107,7 @@ export class ImageUtility { }; }; + // Gets the canvas rendering context of a canvas static getCanvasContext = (canvasRef: RefObject): CanvasRenderingContext2D | null => { if (!canvasRef.current) return null; const ctx = canvasRef.current.getContext('2d'); @@ -106,6 +115,7 @@ export class ImageUtility { return ctx; }; + // Helper for downloading the canvas (for debugging) static downloadCanvas = (canvas: HTMLCanvasElement) => { const url = canvas.toDataURL(); const downloadLink = document.createElement('a'); @@ -116,6 +126,7 @@ export class ImageUtility { downloadLink.remove(); }; + // Download the canvas (for debugging) static downloadImageCanvas = (imgUrl: string) => { const img = new Image(); img.src = imgUrl; @@ -130,12 +141,14 @@ export class ImageUtility { }; }; + // Clears the canvas static clearCanvas = (canvasRef: React.RefObject) => { const ctx = this.getCanvasContext(canvasRef); if (!ctx || !canvasRef.current) return; ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height); }; + // Draws the image to the current canvas static drawImgToCanvas = (img: HTMLImageElement, canvasRef: React.RefObject, width: number, height: number) => { const drawImg = (img: HTMLImageElement) => { const ctx = this.getCanvasContext(canvasRef); @@ -154,7 +167,7 @@ export class ImageUtility { } }; - // The image must be loaded! + // Gets the image mask for the openai endpoint static getCanvasMask = (srcCanvas: HTMLCanvasElement, paddedCanvas: HTMLCanvasElement): HTMLCanvasElement | undefined => { const canvas = document.createElement('canvas'); canvas.width = canvasSize; @@ -162,8 +175,6 @@ export class ImageUtility { const ctx = canvas.getContext('2d'); if (!ctx) return; ctx?.clearRect(0, 0, canvasSize, canvasSize); - // ctx.fillStyle = bgColor; - // ctx.fillRect(0, 0, canvasSize, canvasSize); ctx.drawImage(paddedCanvas, 0, 0); // extract and set padding data @@ -181,6 +192,7 @@ export class ImageUtility { return canvas; }; + // Fills in the blank areas of the image with an image reflection (to fill in a square-shaped canvas) static drawHorizontalReflection = (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, xOffset: number) => { const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; @@ -209,6 +221,7 @@ export class ImageUtility { ctx.putImageData(imageData, 0, 0); }; + // Fills in the blank areas of the image with an image reflection (to fill in a square-shaped canvas) static drawVerticalReflection = (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, yOffset: number) => { const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; @@ -237,7 +250,7 @@ export class ImageUtility { ctx.putImageData(imageData, 0, 0); }; - // The image must be loaded! + // Gets the unaltered (besides filling in padding) version of the image for the api call static getCanvasImg = (img: HTMLImageElement): HTMLCanvasElement | undefined => { const canvas = document.createElement('canvas'); canvas.width = canvasSize; diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts index 1fe151b46..dc94a9368 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts @@ -1,4 +1,3 @@ -// constants export const canvasSize = 512; export const freeformRenderSize = 300; export const offsetDistanceY = freeformRenderSize + 400; diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillInterfaces.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillInterfaces.ts index 83a21a1a5..1e7801056 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillInterfaces.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillInterfaces.ts @@ -1,4 +1,3 @@ -// interfaces export interface CursorData { x: number; y: number; -- cgit v1.2.3-70-g09d2 From 6913400a92d518989e831926567429c31703e4b4 Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Fri, 18 Aug 2023 18:46:27 -0400 Subject: cleanup --- .../views/nodes/formattedText/FormattedTextBox.tsx | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 2562c48e4..da277826a 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -907,7 +907,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { if (resIndex < newText.length) { const marks = this._editorView?.state.storedMarks ?? []; - // if (!marks) return; this._editorView?.dispatch(this._editorView.state.tr.setStoredMarks(marks).insertText(newText[resIndex]).setStoredMarks(marks)); setTimeout(() => { this.animateRes(resIndex + 1, newText); @@ -915,19 +914,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - // const state = this._editorView?.state; - // if (!state) return; - // const to = state.selection.to; - // const updated = TextSelection.create(state.doc, to, to); - // this._editorView?.dispatch(state.tr.setSelection(updated).insertText('\n', to)); - // this._editorView?.dispatch(this._editorView.state.tr.setStoredMarks(marks).insertText('\nTesting').setStoredMarks(marks)); - // console.log('After ', this._editorView?.state.storedMarks); try { let res = await gptAPICall((this.dataDoc.text as RichTextField)?.Text, GPTCallType.COMPLETION); if (!res) { @@ -1243,13 +1230,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent Date: Fri, 18 Aug 2023 19:20:55 -0400 Subject: fix original image click --- src/client/views/nodes/generativeFill/GenerativeFill.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index 998961f20..ce6ce672f 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -63,6 +63,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD const currImg = useRef(null); // the unedited version of each generation (parent) const originalImg = useRef(null); + const originalDoc = useRef(null); // stores history of data urls const undoStack = useRef([]); // stores redo stack @@ -186,6 +187,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD childrenDocs.current = []; currImg.current = null; originalImg.current = null; + originalDoc.current = null; undoStack.current = []; redoStack.current = []; ImageUtility.clearCanvas(canvasRef); @@ -291,6 +293,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD } originalImg.current = currImg.current; + originalDoc.current = parentDoc.current; const { urls } = res as APISuccess; const imgUrls = await Promise.all(urls.map(url => ImageUtility.convertImgToCanvasUrl(url, canvasDims.width, canvasDims.height))); const imgRes = await Promise.all( @@ -302,9 +305,9 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD setEdits(imgRes); const image = new Image(); image.src = imgUrls[0]; - ImageUtility.drawImgToCanvas(image, canvasRef, canvasDims.width, canvasDims.height); currImg.current = image; + parentDoc.current = imgRes[0][1] as Doc; } catch (err) { console.log(err); } @@ -511,6 +514,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD key={i} width={75} src={edit[0] as string} + style={{ cursor: 'pointer' }} onClick={async () => { const img = new Image(); img.src = edit[0] as string; @@ -545,6 +549,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD img.src = originalImg.current.src; ImageUtility.drawImgToCanvas(img, canvasRef, canvasDims.width, canvasDims.height); currImg.current = img; + parentDoc.current = originalDoc.current; }} />
-- cgit v1.2.3-70-g09d2 From c9fe812391b68b1337f50d4e572a18640e39ff3b Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Fri, 18 Aug 2023 19:50:18 -0400 Subject: changed dims to 1024 --- .../views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts | 2 +- .../nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts index c6ecedb6b..2ede625f6 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts @@ -75,7 +75,7 @@ export class ImageUtility { fd.append('image', imgBlob, 'image.png'); fd.append('mask', maskBlob, 'mask.png'); fd.append('prompt', prompt); - fd.append('size', '512x512'); + fd.append('size', '1024x1024'); fd.append('n', n ? JSON.stringify(n) : '1'); fd.append('response_format', 'b64_json'); diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts index dc94a9368..4772304bc 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts @@ -1,4 +1,4 @@ -export const canvasSize = 512; +export const canvasSize = 1024; export const freeformRenderSize = 300; export const offsetDistanceY = freeformRenderSize + 400; export const offsetX = 200; -- cgit v1.2.3-70-g09d2 From 83dbf518c73debb52c6b1994beef5473dce739f7 Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Tue, 22 Aug 2023 13:09:51 -0400 Subject: global showLinkLines toggle --- src/client/util/SettingsManager.tsx | 10 ++++++++++ .../collectionFreeForm/CollectionFreeFormLinkView.tsx | 4 ++-- .../collectionFreeForm/CollectionFreeFormLinksView.tsx | 1 + src/client/views/nodes/DocumentView.tsx | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 6acba8af4..8fff069f4 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -179,6 +179,7 @@ export class SettingsManager extends React.Component<{}> { } @computed get formatsContent() { + console.log(BoolCast(Doc.UserDoc().showLinkLines)); return (
{ size={Size.XSMALL} color={this.userColor} /> + (Doc.UserDoc().showLinkLines = !Doc.UserDoc().showLinkLines)} + toggleStatus={BoolCast(Doc.UserDoc().showLinkLines)} + size={Size.XSMALL} + color={this.userColor} + />
); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index fb8ec93b2..fd9aa3fa5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -59,7 +59,7 @@ export class CollectionFreeFormLinkView extends React.Component (!LinkDocs.length || !linkDoc.link_displayLine) && (this._opacity = 0.05)), + action(() => (!LinkDocs.length || !(linkDoc.link_displayLine || Doc.UserDoc().showLinkLines)) && (this._opacity = 0.05)), 750 ); // this will unhighlight the link line. const a = A.ContentDiv.getBoundingClientRect(); @@ -269,7 +269,7 @@ export class CollectionFreeFormLinkView extends React.Component diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 420e6a318..6013551d9 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -6,6 +6,7 @@ import { LightboxView } from '../../LightboxView'; import './CollectionFreeFormLinksView.scss'; import { CollectionFreeFormLinkView } from './CollectionFreeFormLinkView'; import React = require('react'); +import { LinkManager } from '../../../util/LinkManager'; @observer export class CollectionFreeFormLinksView extends React.Component { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 39c8d3348..533a047b1 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -993,7 +993,7 @@ export class DocumentViewInternal extends DocComponent d.link_displayLine); + const filtered = DocUtils.FilterDocs(this.directLinks, this.props.childFilters?.() ?? [], []).filter(d => d.link_displayLine || Doc.UserDoc().showLinkLines); return filtered.map(link => (
Date: Tue, 22 Aug 2023 14:35:14 -0400 Subject: added tooltips for tree view sorting. added close buttons for header bar.and added new tabs to header bar. fixed rtf markup startup error. --- src/client/util/RTFMarkup.tsx | 2 +- src/client/views/StyleProvider.tsx | 9 ++++---- .../views/collections/CollectionDockingView.tsx | 2 ++ src/client/views/collections/TreeView.tsx | 27 +++++++++++----------- .../CollectionMulticolumnView.scss | 6 +++++ .../CollectionMulticolumnView.tsx | 7 +++++- 6 files changed, 33 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx index afc880a7b..a0fc617ab 100644 --- a/src/client/util/RTFMarkup.tsx +++ b/src/client/util/RTFMarkup.tsx @@ -31,7 +31,7 @@ export class RTFMarkup extends React.Component<{}> { */ @computed get cheatSheet() { return ( -
+

{`wiki:phrase`} {` display wikipedia page for entered text (terminate with carriage return)`} diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index c2dcf071d..24a269927 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -109,10 +109,10 @@ export function DefaultStyleProvider(doc: Opt, props: Opt }; - allSorts[TreeSort.Up] = { color: 'crimson', icon: }; + allSorts[TreeSort.AlphaDown] = { color: Colors.MEDIUM_BLUE, icon: }; + allSorts[TreeSort.AlphaUp] = { color: 'crimson', icon: }; if (doc?._type_collection === CollectionViewType.Freeform) allSorts[TreeSort.Zindex] = { color: 'green', icon: 'Z' }; - allSorts[TreeSort.None] = { color: 'darkgray', icon: }; + allSorts[TreeSort.WhenAdded] = { color: 'darkgray', icon: }; return allSorts; case StyleProp.Highlighting: if (doc && (Doc.IsSystem(doc) || doc.type === DocumentType.FONTICON)) return undefined; @@ -166,8 +166,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt { const expandKey = this.treeViewExpandedView; const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; icon: JSX.Element | string } }) ?? {}; if (['links', 'annotations', 'embeddings', this.fieldKey].includes(expandKey)) { - const sorting = StrCast(this.doc.treeView_SortCriterion, TreeSort.None); + const sorting = StrCast(this.doc.treeView_SortCriterion, TreeSort.WhenAdded); const sortKeys = Object.keys(sortings); const curSortIndex = Math.max( 0, @@ -578,10 +578,11 @@ export class TreeView extends React.Component { return (

{!docs?.length || this.props.AddToMap /* hack to identify pres box trees */ ? null : ( -
+
{ downX = e.clientX; @@ -600,8 +601,8 @@ export class TreeView extends React.Component {
    { downX = e.clientX; downY = e.clientY; @@ -1089,7 +1090,7 @@ export class TreeView extends React.Component { }; @computed get renderBorder() { - const sorting = StrCast(this.doc.treeView_SortCriterion, TreeSort.None); + const sorting = StrCast(this.doc.treeView_SortCriterion, TreeSort.WhenAdded); const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) ?? {}) as { [key: string]: { color: string; label: string } }; return (
    @@ -1131,7 +1132,7 @@ export class TreeView extends React.Component { public static sortDocs(childDocs: Doc[], criterion: string | undefined) { const docs = childDocs.slice(); - if (criterion !== TreeSort.None) { + if (criterion !== TreeSort.WhenAdded) { const sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => { const reN = /[0-9]*$/; const aA = a.replace(reN, '') ? a.replace(reN, '') : +a; // get rid of trailing numbers @@ -1146,8 +1147,8 @@ export class TreeView extends React.Component { } }; docs.sort(function (d1, d2): 0 | 1 | -1 { - const a = criterion === TreeSort.Up ? d2 : d1; - const b = criterion === TreeSort.Up ? d1 : d2; + const a = criterion === TreeSort.AlphaUp ? d2 : d1; + const b = criterion === TreeSort.AlphaUp ? d1 : d2; const first = a[criterion === TreeSort.Zindex ? 'zIndex' : 'title']; const second = b[criterion === TreeSort.Zindex ? 'zIndex' : 'title']; if (typeof first === 'number' && typeof second === 'number') return first - second > 0 ? 1 : -1; @@ -1198,7 +1199,7 @@ export class TreeView extends React.Component { childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result); } - const docs = TreeView.sortDocs(childDocs, StrCast(treeView_Parent.treeView_SortCriterion, TreeSort.None)); + const docs = TreeView.sortDocs(childDocs, StrCast(treeView_Parent.treeView_SortCriterion, TreeSort.WhenAdded)); const rowWidth = () => panelWidth() - treeBulletWidth() * (treeView.props.NativeDimScaling?.() || 1); const treeView_Refs = new Map(); return docs diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss index f87a06033..cb0d5e03f 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss @@ -9,6 +9,12 @@ flex-direction: column; width: 100%; align-items: center; + position: relative; + > .iconButton-container { + top: 0; + left: 0; + position: absolute; + } .contentFittingDocumentView { width: unset; diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 10532b9d9..80da4e1a2 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -1,3 +1,5 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Button } from 'browndash-components'; import { action, computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -6,7 +8,7 @@ import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types import { returnFalse } from '../../../../Utils'; import { DragManager, dropActionType } from '../../../util/DragManager'; import { Transform } from '../../../util/Transform'; -import { undoBatch } from '../../../util/UndoManager'; +import { undoable, undoBatch } from '../../../util/UndoManager'; import { DocumentView } from '../../nodes/DocumentView'; import { CollectionSubView } from '../CollectionSubView'; import './CollectionMulticolumnView.scss'; @@ -301,6 +303,9 @@ export class CollectionMulticolumnView extends CollectionSubView() { collector.push(
    {this.getDisplayDoc(layout, dxf, docwidth, docheight, shouldNotScale)} +
    , Date: Tue, 22 Aug 2023 14:37:21 -0400 Subject: from last --- .../views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 6013551d9..420e6a318 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -6,7 +6,6 @@ import { LightboxView } from '../../LightboxView'; import './CollectionFreeFormLinksView.scss'; import { CollectionFreeFormLinkView } from './CollectionFreeFormLinkView'; import React = require('react'); -import { LinkManager } from '../../../util/LinkManager'; @observer export class CollectionFreeFormLinksView extends React.Component { -- cgit v1.2.3-70-g09d2 From 98e8e59d4cac428ee55db478f7402a7c12eb5ce5 Mon Sep 17 00:00:00 2001 From: geireann Date: Tue, 22 Aug 2023 14:38:19 -0400 Subject: from last --- src/client/util/SettingsManager.tsx | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 8fff069f4..453aba81a 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -179,7 +179,6 @@ export class SettingsManager extends React.Component<{}> { } @computed get formatsContent() { - console.log(BoolCast(Doc.UserDoc().showLinkLines)); return (
    Date: Tue, 22 Aug 2023 15:32:06 -0400 Subject: tweaks --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 91a78c2af..f5cc1eb53 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1600,7 +1600,7 @@ export class CollectionFreeFormView extends CollectionSubView this.rootDoc.fitContentOnce, fitContentOnce => { if (fitContentOnce) this.fitContentOnce(); - this.rootDoc.fitContentOnce = false; + this.rootDoc.fitContentOnce = undefined; }, { fireImmediately: true, name: 'fitContent' } ); diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 0e230e19b..a2cf3bc25 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -426,7 +426,7 @@ export class FontIconBox extends DocComponent() { case ButtonType.DropdownButton: return this.dropdownButton; case ButtonType.MultiToggleButton: return this.multiToggleButton; case ButtonType.ToggleButton: return this.toggleButton; - case ButtonType.IconClickButton: return this.iconClickButton; + case ButtonType.IconClickButton: return this.iconClickButton; case ButtonType.ClickButton: case ButtonType.ToolButton: return ; case ButtonType.TextButton: return
    {this.getDashboards(this.selectedDashboardGroup).map(dashboard => { - const href = ImageCast(dashboard.thumb)?.url.href; + const href = ImageCast(dashboard.thumb)?.url?.href; const shared = Object.keys(dashboard[DocAcl]) .filter(key => key !== `acl-${Doc.CurrentUserEmailNormalized}` && !['acl-Me', 'acl-Guest'].includes(key)) .some(key => dashboard[DocAcl][key] !== AclPrivate); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 95f88f14e..e15d57306 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -551,7 +551,7 @@ export class CollectionDockingView extends CollectionSubView() { }; render() { - const href = ImageCast(this.rootDoc.thumb)?.url.href; + const href = ImageCast(this.rootDoc.thumb)?.url?.href; return this.props.renderDepth > -1 ? (
    {href ? ( diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js index eb8064780..60d997120 100644 --- a/src/client/views/nodes/WebBoxRenderer.js +++ b/src/client/views/nodes/WebBoxRenderer.js @@ -42,7 +42,7 @@ var ForeignHtmlRenderer = function (styleSheets) { url = CorsProxy(new URL(webUrl).origin + inurl); } else if (!inurl.startsWith('http') && !inurl.startsWith('//')) { url = CorsProxy(webUrl + '/' + inurl); - } else if (inurl.startsWith('https')) { + } else if (inurl.startsWith('https') && !inurl.startsWith("https://dashblobstore.blob.core.windows.net")) { url = CorsProxy(inurl); } xhr.open('GET', url); -- cgit v1.2.3-70-g09d2 From f8c2d9d029f129eb595677b1e8a09ff1ebd5880c Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Wed, 23 Aug 2023 23:07:18 -0400 Subject: branching fix --- src/client/views/nodes/generativeFill/GenerativeFill.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index ce6ce672f..7230aa9b1 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -181,6 +181,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD // cleanup return () => { + setInput(''); setEdited(false); newCollectionRef.current = null; parentDoc.current = null; @@ -268,8 +269,8 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD // create first image if (!newCollectionRef.current) { if (!isNewCollection && imageRootDoc) { - // new collection stays null - parentDoc.current = imageRootDoc; + // if the parent hasn't been set yet + if (!parentDoc.current) parentDoc.current = imageRootDoc; } else { if (!(originalImg.current && imageRootDoc)) return; // create new collection and add it to the view -- cgit v1.2.3-70-g09d2 From 1a87cabca08f8919539644c439f38a9da9f7815e Mon Sep 17 00:00:00 2001 From: geireann Date: Wed, 23 Aug 2023 23:25:49 -0400 Subject: protecting against bad urls especially after generating thumbnails --- src/client/views/nodes/ImageBox.tsx | 5 +++-- src/client/views/nodes/WebBoxRenderer.js | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 44da98f75..6595689f7 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -287,10 +287,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent Date: Wed, 23 Aug 2023 23:38:19 -0400 Subject: fixed creating thumbnails to not break when one of the rendered items is an image stored on the same server. --- src/client/views/nodes/WebBoxRenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js index 321cf3638..425ef3e54 100644 --- a/src/client/views/nodes/WebBoxRenderer.js +++ b/src/client/views/nodes/WebBoxRenderer.js @@ -42,7 +42,7 @@ var ForeignHtmlRenderer = function (styleSheets) { url = CorsProxy(new URL(webUrl).origin + inurl); } else if (!inurl.startsWith('http') && !inurl.startsWith('//')) { url = CorsProxy(webUrl + '/' + inurl); - } else if (inurl.startsWith('https')) { + } else if (inurl.startsWith('https') && !inurl.startsWith(window.location.origin)) { url = CorsProxy(inurl); } xhr.open('GET', url); -- cgit v1.2.3-70-g09d2 From 9a9ee6058941dc89eb5719ef4fdd66a4743698fd Mon Sep 17 00:00:00 2001 From: geireann Date: Thu, 24 Aug 2023 10:37:07 -0400 Subject: removed iconClickButton and reverted fit All menu item --- src/client/util/CurrentUserUtils.ts | 14 ++++---- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 37 +++------------------- 2 files changed, 11 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 9322cda4b..62b99d752 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -629,14 +629,14 @@ export class CurrentUserUtils { } static viewTools(): Button[] { return [ - { title: "Snap", icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"snaplines", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform - { title: "Grid", icon: "border-all", toolTip: "Show Grid", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"grid", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform - { title: "View All", icon: "object-group", toolTip: "Fit all Docs to View", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform + { title: "Snap", icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"snaplines", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform + { title: "Grid", icon: "border-all", toolTip: "Show Grid", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"grid", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform + { title: "View All", icon: "object-group", toolTip: "Fit all Docs to View",btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform // want the same style as toggle button, but don't want it to act as an actual toggle, so set disableToggle to true, - { title: "Fit All", icon: "arrows-left-right", toolTip: "Fit all Docs to View (persistent)", btnType: ButtonType.IconClickButton, ignoreClick: false, expertMode: false, toolType:"viewAllPersist", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform - { title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"clusters", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform - { title: "Cards", icon: "brain", toolTip: "Flashcards", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"flashcards", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform - { title: "Arrange",icon:"arrow-down-short-wide",toolTip:"Toggle Auto Arrange", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"arrange", funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform + { title: "Fit All", icon: "arrows-left-right", toolTip: "Fit all Docs to View (persistent)", btnType: ButtonType.ClickButton, ignoreClick: false, expertMode: false, toolType:"viewAllPersist", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform + { title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"clusters", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform + { title: "Cards", icon: "brain", toolTip: "Flashcards", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"flashcards", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform + { title: "Arrange", icon:"arrow-down-short-wide",toolTip:"Toggle Auto Arrange", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"arrange", funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform ] } static textTools():Button[] { diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index a2cf3bc25..47dac651d 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -1,12 +1,13 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, MultiToggle, ColorPicker, Dropdown, DropdownType, EditableText, IconButton, IListItemProps, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components'; +import { Button, ColorPicker, Dropdown, DropdownType, EditableText, IconButton, IListItemProps, MultiToggle, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; import { ScriptField } from '../../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; +import { Utils } from '../../../../Utils'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { SelectionManager } from '../../../util/SelectionManager'; import { undoable, UndoManager } from '../../../util/UndoManager'; @@ -14,14 +15,12 @@ import { ContextMenu } from '../../ContextMenu'; import { DocComponent } from '../../DocComponent'; import { EditableView } from '../../EditableView'; import { Colors } from '../../global/globalEnums'; +import { SelectedDocView } from '../../selectedDoc'; import { StyleProp } from '../../StyleProvider'; -import { FieldView, FieldViewProps } from '../FieldView'; import { OpenWhere } from '../DocumentView'; +import { FieldView, FieldViewProps } from '../FieldView'; import { RichTextMenu } from '../formattedText/RichTextMenu'; import './FontIconBox.scss'; -import { SelectedDocView } from '../../selectedDoc'; -import { Utils } from '../../../../Utils'; -import { FaAlignCenter, FaAlignJustify, FaAlignLeft, FaAlignRight } from 'react-icons/fa'; export enum ButtonType { TextButton = 'textBtn', @@ -29,7 +28,6 @@ export enum ButtonType { DropdownList = 'dropdownList', DropdownButton = 'dropdownBtn', ClickButton = 'clickBtn', - IconClickButton = 'iconClickBtn', ToggleButton = 'toggleBtn', ColorButton = 'colorBtn', ToolButton = 'toolBtn', @@ -353,32 +351,6 @@ export class FontIconBox extends DocComponent() { ); } - @computed get iconClickButton() { - // Determine the type of toggle button - const buttonText: string = StrCast(this.rootDoc.buttonText); - const tooltip: string = StrCast(this.rootDoc.toolTip); - - const script = ScriptCast(this.rootDoc.onClick); - const toggleStatus = script ? script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: undefined, _readOnly_: true }).result : false; - // Colors - const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - - return ( - script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: !toggleStatus, _readOnly_: false })} - /> - ); - } - /** * Default */ @@ -426,7 +398,6 @@ export class FontIconBox extends DocComponent() { case ButtonType.DropdownButton: return this.dropdownButton; case ButtonType.MultiToggleButton: return this.multiToggleButton; case ButtonType.ToggleButton: return this.toggleButton; - case ButtonType.IconClickButton: return this.iconClickButton; case ButtonType.ClickButton: case ButtonType.ToolButton: return ; case ButtonType.TextButton: return
    ); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index c4b3f79a7..2c8e97512 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -36,8 +36,6 @@ import { FieldView, FieldViewProps } from './FieldView'; import './ImageBox.scss'; import { PinProps, PresBox } from './trails'; import React = require('react'); -import Color = require('color'); -import { MainView } from '../MainView'; export const pageSchema = createSchema({ googlePhotosUrl: 'string', @@ -56,6 +54,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent boolean) | undefined; + @action public static setImageEditorOpen(open: boolean) {ImageBox.imageEditorOpen = open;} + @action public static setImageEditorSource(source: string) {ImageBox.imageEditorSource = source;} private _ignoreScroll = false; private _forcedScroll = false; private _dropDisposer?: DragManager.DragDropDisposer; @@ -250,10 +255,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent { - MainView.Instance.setImageEditorOpen(true); - MainView.Instance.setImageEditorSource(this.choosePath(field.url)); - MainView.Instance.addDoc = this.props.addDocument; - MainView.Instance.imageRootDoc = this.rootDoc; + ImageBox.setImageEditorOpen(true); + ImageBox.setImageEditorSource(this.choosePath(field.url)); + ImageBox.addDoc = this.props.addDocument; + ImageBox.imageRootDoc = this.rootDoc; }), icon: 'pencil-alt', }); diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index 7230aa9b1..9c03600cf 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -20,6 +20,7 @@ import { OpenWhereMod } from '../DocumentView'; import Buttons from './GenerativeFillButtons'; import { List } from '../../../../fields/List'; import { CgClose } from 'react-icons/cg'; +import { ImageBox } from '../ImageBox'; enum BrushStyle { ADD, @@ -408,8 +409,8 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD // Closes the editor view const handleViewClose = () => { - MainView.Instance.setImageEditorOpen(false); - MainView.Instance.setImageEditorSource(''); + ImageBox.setImageEditorOpen(false); + ImageBox.setImageEditorSource(''); if (newCollectionRef.current) { newCollectionRef.current.fitContentOnce = true; } -- cgit v1.2.3-70-g09d2 From 50d65b4e805fd779400c710551648541b12ba3e6 Mon Sep 17 00:00:00 2001 From: geireann Date: Thu, 24 Aug 2023 11:43:05 -0400 Subject: made link lines appear in the lightbox view --- src/client/views/MainView.tsx | 2 +- src/client/views/OverlayView.scss | 1 + src/client/views/OverlayView.tsx | 5 ++--- .../collections/collectionFreeForm/CollectionFreeFormLinksView.tsx | 5 ++++- 4 files changed, 8 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index e376c4fdf..490145546 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1006,12 +1006,12 @@ export class MainView extends React.Component { - {this.snapLines} + {/* */}
    ); diff --git a/src/client/views/OverlayView.scss b/src/client/views/OverlayView.scss index 5362bf9f0..9b10d1cf7 100644 --- a/src/client/views/OverlayView.scss +++ b/src/client/views/OverlayView.scss @@ -4,6 +4,7 @@ top: 0; width: 100vw; height: 100vh; + z-index: 1001; // shouold be greater than LightboxView's z-index so that link lines and the presentation mini player appear /* background-color: pink; */ } diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 339507f65..62aa4d1d4 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -1,3 +1,4 @@ + import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; @@ -7,14 +8,12 @@ import { Doc, DocListCast } from '../../fields/Doc'; import { Height, Width } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { NumCast } from '../../fields/Types'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnTrue, setupMoveUpEvents, Utils } from '../../Utils'; import { DocumentType } from '../documents/DocumentTypes'; -import { DocumentManager } from '../util/DocumentManager'; import { DragManager } from '../util/DragManager'; import { Transform } from '../util/Transform'; import { CollectionFreeFormLinksView } from './collections/collectionFreeForm/CollectionFreeFormLinksView'; import { LightboxView } from './LightboxView'; -import { MainView } from './MainView'; import { DocumentView, DocumentViewInternal } from './nodes/DocumentView'; import './OverlayView.scss'; import { DefaultStyleProvider } from './StyleProvider'; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 420e6a318..7c869af24 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -12,7 +12,10 @@ export class CollectionFreeFormLinksView extends React.Component { @computed get uniqueConnections() { return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews)) .filter(c => !LightboxView.LightboxDoc || (LightboxView.IsLightboxDocView(c.a.docViewPath) && LightboxView.IsLightboxDocView(c.b.docViewPath))) - .map(c => ); + .map(c => { + console.log("got a connectoin", c) + return ; + }); } render() { -- cgit v1.2.3-70-g09d2 From 859d2d942b7766ed8052ff66be991e4d593982ff Mon Sep 17 00:00:00 2001 From: geireann Date: Thu, 24 Aug 2023 12:10:07 -0400 Subject: fixed gpt anno to not create an anchor unless actually requeted. --- src/client/views/nodes/WebBox.tsx | 5 ----- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 7 ------- 2 files changed, 12 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index a1e46b69b..febf8341e 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -358,11 +358,6 @@ export class WebBox extends ViewBoxAnnotatableComponent { this.sidebarId = id; }; - // pdfs and webpages - @observable - private targetAnchor: Doc | undefined; - @action - public setTargetAnchor = (anchor: Doc) => { - this.targetAnchor = anchor; - }; @observable private imgTargetDoc: Doc | undefined; -- cgit v1.2.3-70-g09d2 From 20e3d33d864f9ee9db2ca65848b0f42a087b699e Mon Sep 17 00:00:00 2001 From: geireann Date: Thu, 24 Aug 2023 12:12:56 -0400 Subject: updated dash version --- src/fields/DocSymbols.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/fields/DocSymbols.ts b/src/fields/DocSymbols.ts index 088903082..856c377fa 100644 --- a/src/fields/DocSymbols.ts +++ b/src/fields/DocSymbols.ts @@ -24,4 +24,4 @@ export const Initializing = Symbol('DocInitializing'); export const ForceServerWrite = Symbol('DocForceServerWrite'); export const CachedUpdates = Symbol('DocCachedUpdates'); -export const DashVersion = 'v0.5.6'; +export const DashVersion = 'v0.5.7'; -- cgit v1.2.3-70-g09d2