aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/DocumentDecorations.tsx7
-rw-r--r--src/client/views/StyleProviderQuiz.tsx2
-rw-r--r--src/client/views/ViewBoxInterface.ts1
-rw-r--r--src/client/views/nodes/ImageBox.tsx21
-rw-r--r--src/client/views/nodes/calendarBox/CalendarBox.tsx2
-rw-r--r--src/client/views/nodes/imageEditor/ImageEditor.tsx83
-rw-r--r--src/client/views/nodes/imageEditor/ImageEditorButtons.tsx4
-rw-r--r--src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts1
-rw-r--r--src/client/views/nodes/imageEditor/imageEditorUtils/imageEditorInterfaces.ts5
9 files changed, 58 insertions, 68 deletions
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 54ff3904d..d9b6bdf1a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -36,6 +36,7 @@ import { ImageBox } from './nodes/ImageBox';
import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
import { TagsView } from './TagsView';
+import { ImageField } from '../../fields/URLField';
interface DocumentDecorationsProps {
PanelWidth: number;
@@ -284,8 +285,8 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
embedding.y = -NumCast(embedding._height) / 2;
CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([embedding], { title: 'Tab for ' + embedding.title }), OpenWhereMod.right);
} else if (e.altKey) {
- // open same document in new tab
- CollectionDockingView.ToggleSplit(selView.Document, OpenWhereMod.right);
+ // open same document in new tab or in custom editor
+ selView.ComponentView?.docEditorView?.() ?? CollectionDockingView.ToggleSplit(selView.Document, OpenWhereMod.right);
} else {
let openDoc = selView.Document;
if (openDoc.layout_fieldKey === 'layout_icon') {
@@ -821,7 +822,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
{hideDeleteButton ? null : topBtn('close', 'times', undefined, () => this.onCloseClick(true), 'Close')}
{hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, () => this.onCloseClick(undefined), 'Minimize')}
{titleArea}
- {hideOpenButton ? <div /> : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
+ {hideOpenButton ? <div /> : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection, opption: in editor view)')}
</div>
{hideResizers ? null : (
<>
diff --git a/src/client/views/StyleProviderQuiz.tsx b/src/client/views/StyleProviderQuiz.tsx
index d8eeb3490..db9ab831a 100644
--- a/src/client/views/StyleProviderQuiz.tsx
+++ b/src/client/views/StyleProviderQuiz.tsx
@@ -125,7 +125,7 @@ export namespace styleProviderQuiz {
try {
const hrefBase64 = await createCanvas(img);
const response = await gptImageLabel(hrefBase64, 'Make flashcards out of this image with each question and answer labeled as "question" and "answer". Do not label each flashcard and do not include asterisks: ');
- AnchorMenu.Instance.transferToFlashcard(response, NumCast(img.layoutDoc['x']), NumCast(img.layoutDoc['y']));
+ AnchorMenu.Instance.transferToFlashcard(response, NumCast(img.layoutDoc.x), NumCast(img.layoutDoc.y));
} catch (error) {
console.log('Error', error);
}
diff --git a/src/client/views/ViewBoxInterface.ts b/src/client/views/ViewBoxInterface.ts
index 30da8c616..b943259ff 100644
--- a/src/client/views/ViewBoxInterface.ts
+++ b/src/client/views/ViewBoxInterface.ts
@@ -23,6 +23,7 @@ export abstract class ViewBoxInterface<P> extends ObservableReactComponent<React
}
promoteCollection?: () => void; // moves contents of collection to parent
hasChildDocs?: () => Doc[];
+ docEditorView?: () => void;
updateIcon?: (usePanelDimensions?: boolean) => Promise<void>; // updates the icon representation of the document
getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
restoreView?: (viewSpec: Doc) => boolean; // DEPRECATED: do not use, it will go away. see PresBox.restoreTargetDocView
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index ff6baf199..dd28832f9 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -316,6 +316,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return cropping;
};
+ docEditorView = action(() => {
+ const field = Cast(this.dataDoc[this.fieldKey], ImageField);
+ if (field) {
+ ImageEditorData.Open = true;
+ ImageEditorData.Source = this.choosePath(field.url);
+ ImageEditorData.AddDoc = this._props.addDocument;
+ ImageEditorData.RootDoc = this.Document;
+ }
+ });
+
specificContextMenu = (): void => {
const field = Cast(this.dataDoc[this.fieldKey], ImageField);
if (field) {
@@ -353,16 +363,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
icon: 'expand-arrows-alt',
});
funcs.push({ description: 'Copy path', event: () => ClientUtils.CopyText(this.choosePath(field.url)), icon: 'copy' });
- funcs.push({
- description: 'Open Image Editor',
- event: action(() => {
- ImageEditorData.Open = true;
- ImageEditorData.Source = this.choosePath(field.url);
- ImageEditorData.AddDoc = this._props.addDocument;
- ImageEditorData.RootDoc = this.Document;
- }),
- icon: 'pencil-alt',
- });
+ funcs.push({ description: 'Open Image Editor', event: this.docEditorView, icon: 'pencil-alt' });
this.layoutDoc.ai &&
funcs.push({
description: 'Regenerate AI Image',
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index c1f6f5859..009eb82cd 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -6,7 +6,7 @@ import interactionPlugin from '@fullcalendar/interaction';
import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { dateRangeStrToDates, simulateMouseClick } from '../../../../ClientUtils';
+import { dateRangeStrToDates } from '../../../../ClientUtils';
import { Doc } from '../../../../fields/Doc';
import { BoolCast, NumCast, StrCast } from '../../../../fields/Types';
import { CollectionSubView, SubCollectionViewProps } from '../../collections/CollectionSubView';
diff --git a/src/client/views/nodes/imageEditor/ImageEditor.tsx b/src/client/views/nodes/imageEditor/ImageEditor.tsx
index 6b1d05031..3c0ab3da5 100644
--- a/src/client/views/nodes/imageEditor/ImageEditor.tsx
+++ b/src/client/views/nodes/imageEditor/ImageEditor.tsx
@@ -90,18 +90,8 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
* @param type The new tool type we are changing to
*/
const changeTool = (type: ImageToolType) => {
- switch (type) {
- case ImageToolType.GenerativeFill:
- setCurrTool(genFillTool);
- setCursorData(prev => ({ ...prev, width: genFillTool.sliderDefault as number }));
- break;
- case ImageToolType.Cut:
- setCurrTool(cutTool);
- setCursorData(prev => ({ ...prev, width: cutTool.sliderDefault as number }));
- break;
- default:
- break;
- }
+ setCurrToolType(type);
+ setCursorData(prev => ({ ...prev, width: currTool().sliderDefault as number }));
};
// Undo and Redo
const handleUndo = () => {
@@ -171,9 +161,8 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
// handles brushing on pointer movement
useEffect(() => {
- if (!isBrushing) return undefined;
const canvas = canvasRef.current;
- if (!canvas) return undefined;
+ if (!isBrushing || !canvas) return undefined;
const ctx = ImageUtility.getCanvasContext(canvasRef);
if (!ctx) return undefined;
@@ -188,33 +177,29 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
};
drawingAreaRef.current?.addEventListener('pointermove', handlePointerMove);
- return () => {
- drawingAreaRef.current?.removeEventListener('pointermove', handlePointerMove);
- };
+ return () => drawingAreaRef.current?.removeEventListener('pointermove', handlePointerMove);
}, [isBrushing]);
// first load
useEffect(() => {
- const loadInitial = async () => {
- if (!imageEditorSource || imageEditorSource === '') return;
- const img = new Image();
- const res = await ImageUtility.urlToBase64(imageEditorSource);
- if (!res) return;
- img.src = `data:image/png;base64,${res}`;
-
- img.onload = () => {
- currImg.current = img;
- originalImg.current = img;
- 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 });
- };
- };
-
- loadInitial();
+ if (imageEditorSource && imageEditorSource) {
+ ImageUtility.urlToBase64(imageEditorSource).then(res => {
+ if (res) {
+ const img = new Image();
+ img.src = `data:image/png;base64,${res}`;
+ img.onload = () => {
+ currImg.current = img;
+ originalImg.current = img;
+ 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 });
+ };
+ }
+ });
+ }
// cleanup
return () => {
@@ -300,7 +285,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
if (!canvasMask) return;
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.getEdit(imgBlob, maskBlob, input || 'Fill in the image in the same style', 2);
// create first image
if (!newCollectionRef.current) {
@@ -569,11 +554,15 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
setIsFirstDoc(true);
};
+ function currTool() {
+ return imageEditTools.find(tool => tool.type === currToolType) ?? genFillTool;
+ }
+
// defines the tools and sets current tool
- const genFillTool: ImageEditTool = { type: ImageToolType.GenerativeFill, name: 'Generative Fill', btnText: 'GET EDITS', icon: 'fill', applyFunc: getEdit, sliderMin: 25, sliderMax: 500, sliderDefault: 150 };
- const cutTool: ImageEditTool = { type: ImageToolType.Cut, name: 'Cut', btnText: 'CUT IMAGE', icon: 'scissors', applyFunc: cutImage, sliderMin: 1, sliderMax: 50, sliderDefault: 5 };
+ const genFillTool: ImageEditTool = { type: ImageToolType.GenerativeFill, btnText: 'GET EDITS', icon: 'fill', applyFunc: getEdit, sliderMin: 25, sliderMax: 500, sliderDefault: 150 };
+ const cutTool: ImageEditTool = { type: ImageToolType.Cut, btnText: 'CUT IMAGE', icon: 'scissors', applyFunc: cutImage, sliderMin: 1, sliderMax: 50, sliderDefault: 5 };
const imageEditTools: ImageEditTool[] = [genFillTool, cutTool];
- const [currTool, setCurrTool] = useState<ImageEditTool>(genFillTool);
+ const [currToolType, setCurrToolType] = useState<ImageToolType>(ImageToolType.GenerativeFill);
// the top controls for making a new collection, resetting, and applying edits,
function renderControls() {
@@ -595,7 +584,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
labelPlacement="end"
sx={{ whiteSpace: 'nowrap' }}
/>
- <ApplyFuncButtons onClick={() => currTool.applyFunc(cutType, cursorData.width, edits, isFirstDoc)} loading={loading} onReset={handleReset} btnText={currTool.btnText} />
+ <ApplyFuncButtons onClick={() => currTool().applyFunc(cutType, cursorData.width, edits, isFirstDoc)} loading={loading} onReset={handleReset} btnText={currTool().btnText} />
<IconButton color={activeColor} tooltip="close" icon={<CgClose size="16px" />} onClick={handleViewClose} />
</div>
</div>
@@ -607,8 +596,8 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
return (
<div className="sideControlsContainer" style={{ backgroundColor: bgColor }}>
<div className="sideControls">
- <div className="imageToolsContainer">{imageEditTools.map(tool => ImageToolButton(tool, tool.type === currTool.type, changeTool))}</div>
- {currTool.type == ImageToolType.Cut && (
+ <div className="imageToolsContainer">{imageEditTools.map(tool => ImageToolButton(tool, tool.type === currTool().type, changeTool))}</div>
+ {currTool().type == ImageToolType.Cut && (
<div className="cutToolsContainer">
<Button style={{ width: '100%' }} text="Keep in" type={Type.TERT} color={cutType == CutMode.IN ? SettingsManager.userColor : bgColor} onClick={() => setCutType(CutMode.IN)} />
<Button style={{ width: '100%' }} text="Keep out" type={Type.TERT} color={cutType == CutMode.OUT ? SettingsManager.userColor : bgColor} onClick={() => setCutType(CutMode.OUT)} />
@@ -617,7 +606,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
</div>
)}
<div className="sliderContainer" onPointerDown={e => e.stopPropagation()}>
- {currTool.type === ImageToolType.GenerativeFill && (
+ {currTool().type === ImageToolType.GenerativeFill && (
<Slider
sx={{
'& input[type="range"]': {
@@ -633,7 +622,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
onChange={(e, val) => setCursorData(prev => ({ ...prev, width: val as number }))}
/>
)}
- {currTool.type === ImageToolType.Cut && (
+ {currTool().type === ImageToolType.Cut && (
<Slider
sx={{
'& input[type="range"]': {
@@ -780,7 +769,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
{renderSideIcons()}
{renderEditThumbnails()}
</div>
- {currTool.type === ImageToolType.GenerativeFill && renderPromptBox()}
+ {currTool().type === ImageToolType.GenerativeFill && renderPromptBox()}
</div>
);
};
diff --git a/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx b/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx
index 985dc914f..3eaa251f2 100644
--- a/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx
+++ b/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx
@@ -53,10 +53,10 @@ export function ApplyFuncButtons({ loading, onClick: getEdit, onReset, btnText }
export function ImageToolButton(tool: ImageEditTool, isActive: boolean, selectTool: (type: ImageToolType) => void) {
return (
- <div key={tool.name} className="imageEditorButtonContainer">
+ <div key={tool.type} className="imageEditorButtonContainer">
<Button
style={{ width: '100%' }}
- text={tool.name}
+ text={tool.type}
type={Type.TERT}
color={isActive ? SettingsManager.userVariantColor : bgColor}
icon={<FontAwesomeIcon icon={tool.icon} />}
diff --git a/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts b/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts
index ece0f4d7f..1c6a38a24 100644
--- a/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts
+++ b/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts
@@ -87,7 +87,6 @@ export class ImageUtility {
body: fd,
});
const data = await res.json();
- console.log(data.data);
return {
status: 'success',
urls: (data.data as { b64_json: string }[]).map(urlData => `data:image/png;base64,${urlData.b64_json}`),
diff --git a/src/client/views/nodes/imageEditor/imageEditorUtils/imageEditorInterfaces.ts b/src/client/views/nodes/imageEditor/imageEditorUtils/imageEditorInterfaces.ts
index a14b55439..02dbc0312 100644
--- a/src/client/views/nodes/imageEditor/imageEditorUtils/imageEditorInterfaces.ts
+++ b/src/client/views/nodes/imageEditor/imageEditorUtils/imageEditorInterfaces.ts
@@ -13,8 +13,8 @@ export interface Point {
}
export enum ImageToolType {
- GenerativeFill = 'genFill',
- Cut = 'cut',
+ GenerativeFill = 'Generative Fill',
+ Cut = 'Cut',
}
export enum CutMode {
@@ -26,7 +26,6 @@ export enum CutMode {
export interface ImageEditTool {
type: ImageToolType;
- name: string;
btnText: string;
icon: IconProp;
// this is the function that the image tool applies, so it can be defined depending on the tool