aboutsummaryrefslogtreecommitdiff
path: root/src/client/views
diff options
context:
space:
mode:
authoreleanor-park <eleanor_park@brown.edu>2024-07-11 12:08:26 -0400
committereleanor-park <eleanor_park@brown.edu>2024-07-11 12:08:26 -0400
commitb88f3a79b4558b222864b7c925fd0d5086cdcea2 (patch)
tree4f830b4a4dd96948983115e1e5bff07a1a6eb3d1 /src/client/views
parent4438e7fe202ff4091b26f073122e7866ec9abb46 (diff)
Revert "Merge branch 'eleanor-gptdraw' into master"
This reverts commit 4438e7fe202ff4091b26f073122e7866ec9abb46, reversing changes made to 59ca918ea0918b41f1e2fa4b6acb8725ca9b44af.
Diffstat (limited to 'src/client/views')
-rw-r--r--src/client/views/GestureOverlay.tsx28
-rw-r--r--src/client/views/LightboxView.scss24
-rw-r--r--src/client/views/LightboxView.tsx7
-rw-r--r--src/client/views/MainView.tsx5
-rw-r--r--src/client/views/MarqueeAnnotator.tsx146
-rw-r--r--src/client/views/collections/CollectionMenu.scss2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx242
-rw-r--r--src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss14
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx6
-rw-r--r--src/client/views/global/globalScripts.ts4
-rw-r--r--src/client/views/nodes/PDFBox.scss16
-rw-r--r--src/client/views/nodes/PDFBox.tsx37
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx14
-rw-r--r--src/client/views/pdf/PDFViewer.tsx3
-rw-r--r--src/client/views/smartdraw/DrawingPalette.scss11
-rw-r--r--src/client/views/smartdraw/DrawingPalette.tsx89
-rw-r--r--src/client/views/smartdraw/SmartDrawHandler.tsx409
17 files changed, 107 insertions, 950 deletions
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index a1f7712c1..e3e252593 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -240,15 +240,15 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
this._points.length = 0;
switch (shape) {
case Gestures.Rectangle:
- this._points.push({ X: left, Y: top }); // curr pt
- this._points.push({ X: left, Y: top }); // curr first ctrl pt
- this._points.push({ X: right, Y: top }); // next ctrl pt
- this._points.push({ X: right, Y: top }); // next pt
+ this._points.push({ X: left, Y: top });
+ this._points.push({ X: left, Y: top });
+ this._points.push({ X: right, Y: top });
+ this._points.push({ X: right, Y: top });
- this._points.push({ X: right, Y: top }); // next pt
- this._points.push({ X: right, Y: top }); // next first ctrl pt
- this._points.push({ X: right, Y: bottom }); // next next ctrl pt
- this._points.push({ X: right, Y: bottom }); // next next pt
+ this._points.push({ X: right, Y: top });
+ this._points.push({ X: right, Y: top });
+ this._points.push({ X: right, Y: bottom });
+ this._points.push({ X: right, Y: bottom });
this._points.push({ X: right, Y: bottom });
this._points.push({ X: right, Y: bottom });
@@ -291,13 +291,13 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
const radius = Math.max(centerX - Math.min(left, right), centerY - Math.min(top, bottom));
// Dividing the circle into four equal sections, and fitting each section to a cubic Bézier curve.
- this._points.push({ X: centerX, Y: centerY + radius }); // curr pt
- this._points.push({ X: centerX + c * radius, Y: centerY + radius }); // curr first ctrl pt
- this._points.push({ X: centerX + radius, Y: centerY + c * radius }); // next pt ctrl pt
- this._points.push({ X: centerX + radius, Y: centerY }); // next pt
+ this._points.push({ X: centerX, Y: centerY + radius });
+ this._points.push({ X: centerX + c * radius, Y: centerY + radius });
+ this._points.push({ X: centerX + radius, Y: centerY + c * radius });
+ this._points.push({ X: centerX + radius, Y: centerY });
- this._points.push({ X: centerX + radius, Y: centerY }); // next pt
- this._points.push({ X: centerX + radius, Y: centerY - c * radius }); // next first ctrl pt
+ this._points.push({ X: centerX + radius, Y: centerY });
+ this._points.push({ X: centerX + radius, Y: centerY - c * radius });
this._points.push({ X: centerX + c * radius, Y: centerY - radius });
this._points.push({ X: centerX, Y: centerY - radius });
diff --git a/src/client/views/LightboxView.scss b/src/client/views/LightboxView.scss
index 3e65843df..6da5c0338 100644
--- a/src/client/views/LightboxView.scss
+++ b/src/client/views/LightboxView.scss
@@ -1,7 +1,7 @@
.lightboxView-navBtn {
margin: auto;
position: absolute;
- right: 19;
+ right: 10;
top: 10;
background: transparent;
border-radius: 8;
@@ -16,7 +16,7 @@
.lightboxView-tabBtn {
margin: auto;
position: absolute;
- right: 54;
+ right: 45;
top: 10;
background: transparent;
border-radius: 8;
@@ -28,26 +28,10 @@
opacity: 1;
}
}
-.lightboxView-paletteBtn {
- margin: auto;
- position: absolute;
- right: 89;
- top: 10;
- background: transparent;
- border-radius: 8;
- opacity: 0.7;
- width: 25;
- flex-direction: column;
- display: flex;
- &:hover {
- opacity: 1;
- }
-}
-
.lightboxView-penBtn {
margin: auto;
position: absolute;
- right: 124;
+ right: 80;
top: 10;
background: transparent;
border-radius: 8;
@@ -62,7 +46,7 @@
.lightboxView-exploreBtn {
margin: auto;
position: absolute;
- right: 159;
+ right: 115;
top: 10;
background: transparent;
border-radius: 8;
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index e93e4949b..7198c7f05 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -23,7 +23,6 @@ import { DocumentView } from './nodes/DocumentView';
import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere';
import { ScriptingGlobals } from '../util/ScriptingGlobals';
import { OverlayView } from './OverlayView';
-import { DrawingPalette } from './smartdraw/DrawingPalette';
interface LightboxViewProps {
PanelWidth: number;
@@ -60,7 +59,6 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
@observable private _doc: Opt<Doc> = undefined;
@observable private _docTarget: Opt<Doc> = undefined;
@observable private _docView: Opt<DocumentView> = undefined;
- @observable private _showPalette: boolean = false;
@computed get leftBorder() { return Math.min(this._props.PanelWidth / 4, this._props.maxBorder[0]); } // prettier-ignore
@computed get topBorder() { return Math.min(this._props.PanelHeight / 4, this._props.maxBorder[1]); } // prettier-ignore
@@ -204,9 +202,6 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
toggleFitWidth = () => {
this._doc && (this._doc._layout_fitWidth = !this._doc._layout_fitWidth);
};
- togglePalette = () => {
- this._showPalette = !this._showPalette;
- };
togglePen = () => {
Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen;
};
@@ -324,10 +319,8 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
<LightboxTourBtn lightboxDoc={this.lightboxDoc} navBtn={this.renderNavBtn} future={this.future} stepInto={this.stepInto} />
{toggleBtn('lightboxView-navBtn', 'toggle reading view', this._doc?._layout_fitWidth, 'book-open', 'book', this.toggleFitWidth)}
{toggleBtn('lightboxView-tabBtn', 'open document in a tab', false, 'file-download', '', this.downloadDoc)}
- {toggleBtn('lightboxView-paletteBtn', 'toggle annotation palette', this._showPalette === true, 'palette', '', this.togglePalette)}
{toggleBtn('lightboxView-penBtn', 'toggle pen annotation', Doc.ActiveTool === InkTool.Pen, 'pen', '', this.togglePen)}
{toggleBtn('lightboxView-exploreBtn', 'toggle navigate only mode', SnappingManager.ExploreMode, 'globe-americas', '', this.toggleExplore)}
- {this._showPalette && <DrawingPalette />}
</div>
);
}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 30720a3a2..ef1bcfb64 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -76,7 +76,6 @@ import { PresBox } from './nodes/trails';
import { AnchorMenu } from './pdf/AnchorMenu';
import { GPTPopup } from './pdf/GPTPopup/GPTPopup';
import { TopBar } from './topbar/TopBar';
-import { SmartDrawHandler } from './smartdraw/SmartDrawHandler';
const { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } = require('./global/globalCssVariables.module.scss'); // prettier-ignore
const _global = (window /* browser */ || global) /* node */ as any;
@@ -318,7 +317,6 @@ export class MainView extends ObservableReactComponent<{}> {
fa.faCompass,
fa.faSnowflake,
fa.faStar,
- fa.faSplotch,
fa.faMicrophone,
fa.faCircleHalfStroke,
fa.faKeyboard,
@@ -396,7 +394,6 @@ export class MainView extends ObservableReactComponent<{}> {
fa.faArrowsLeftRight,
fa.faPause,
fa.faPen,
- fa.faUserPen,
fa.faPenNib,
fa.faPhone,
fa.faPlay,
@@ -442,7 +439,6 @@ export class MainView extends ObservableReactComponent<{}> {
fa.faEyeDropper,
fa.faPaintRoller,
fa.faBars,
- fa.faBarsStaggered,
fa.faBrush,
fa.faShapes,
fa.faEllipsisH,
@@ -1093,7 +1089,6 @@ export class MainView extends ObservableReactComponent<{}> {
<TaskCompletionBox />
<ContextMenu />
<ImageLabelHandler />
- <SmartDrawHandler />
<AnchorMenu />
<MapAnchorMenu />
<DirectionsAnchorMenu />
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index f06f3efe0..c18ac6738 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -28,7 +28,6 @@ export interface MarqueeAnnotatorProps {
marqueeContainer: HTMLDivElement;
docView: () => DocumentView;
savedAnnotations: () => ObservableMap<number, HTMLDivElement[]>;
- savedTapes: () => ObservableMap<number, HTMLDivElement[]>;
selectionText: () => string;
annotationLayer: HTMLDivElement;
addDocument: (doc: Doc) => boolean;
@@ -75,7 +74,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
backgroundColor: color,
annotationOn: this.props.Document,
title: 'Annotation on ' + this.props.Document.title,
- a,
});
marqueeAnno.x = NumCast(doc.freeform_panX_min) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale;
marqueeAnno.y = NumCast(doc.freeform_panY_min) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale;
@@ -129,140 +127,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
savedAnnoMap.clear();
return textRegionAnno;
};
-
- // @undoBatch
- // makeTapeDocument = (color: string, isLinkButton?: boolean, savedTapes?: ObservableMap<number, HTMLDivElement[]>): Opt<Doc> => {
- // const savedTapeMap = savedTapes?.values() && Array.from(savedTapes?.values()).length ? savedTapes : this.props.savedTapes();
- // if (savedTapeMap.size === 0) return undefined;
- // const tapes = Array.from(savedTapeMap.values())[0];
- // const doc = this.props.Document;
- // const scale = (this.props.annotationLayerScaling?.() || 1) * NumCast(doc._freeform_scale, 1);
- // if (tapes.length && (tapes[0] as any).marqueeing) {
- // const anno = tapes[0];
- // const containerOffset = this.props.containerOffset?.() || [0, 0];
- // const tape = Docs.Create.FreeformDocument([], {
- // onClick: isLinkButton ? FollowLinkScript() : undefined,
- // backgroundColor: color,
- // annotationOn: this.props.Document,
- // title: 'Tape on ' + this.props.Document.title,
- // });
- // tape.x = NumCast(doc.freeform_panX_min) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale;
- // tape.y = NumCast(doc.freeform_panY_min) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale;
- // tape._height = parseInt(anno.style.height || '0') / scale;
- // tape._width = parseInt(anno.style.width || '0') / scale;
- // anno.remove();
- // savedTapeMap.clear();
- // return tape;
- // }
-
- // const textRegionAnno = Docs.Create.ConfigDocument({
- // annotationOn: this.props.Document,
- // text: this.props.selectionText() as any, // text want an RTFfield, but strings are acceptable, too.
- // text_html: this.props.selectionText() as any,
- // backgroundColor: 'transparent',
- // presentation_duration: 2100,
- // presentation_transition: 500,
- // presentation_zoomText: true,
- // title: '>' + this.props.Document.title,
- // });
- // const textRegionAnnoProto = textRegionAnno[DocData];
- // let minX = Number.MAX_VALUE;
- // let maxX = -Number.MAX_VALUE;
- // let minY = Number.MAX_VALUE;
- // let maxY = -Number.MIN_VALUE;
- // const annoRects: string[] = [];
- // savedAnnoMap.forEach((value: HTMLDivElement[]) =>
- // value.forEach(anno => {
- // const x = parseInt(anno.style.left ?? '0');
- // const y = parseInt(anno.style.top ?? '0');
- // const height = parseInt(anno.style.height ?? '0');
- // const width = parseInt(anno.style.width ?? '0');
- // annoRects.push(`${x}:${y}:${width}:${height}`);
- // anno.remove();
- // minY = Math.min(NumCast(y), minY);
- // minX = Math.min(NumCast(x), minX);
- // maxY = Math.max(NumCast(y) + NumCast(height), maxY);
- // maxX = Math.max(NumCast(x) + NumCast(width), maxX);
- // })
- // );
-
- // textRegionAnnoProto.y = Math.max(minY, 0);
- // textRegionAnnoProto.x = Math.max(minX, 0);
- // textRegionAnnoProto.height = Math.max(maxY, 0) - Math.max(minY, 0);
- // textRegionAnnoProto.width = Math.max(maxX, 0) - Math.max(minX, 0);
- // textRegionAnnoProto.backgroundColor = color;
- // // mainAnnoDocProto.text = this._selectionText;
- // textRegionAnnoProto.text_inlineAnnotations = new List<string>(annoRects);
- // textRegionAnnoProto.opacity = 0;
- // textRegionAnnoProto.layout_unrendered = true;
- // savedAnnoMap.clear();
- // return textRegionAnno;
- // };
-
- @undoBatch
- makeTapeDocument = (color: string, isLinkButton?: boolean, savedTapes?: ObservableMap<number, HTMLDivElement[]>): Opt<Doc> => {
- // const savedAnnoMap = savedTapes?.values() && Array.from(savedTapes?.values()).length ? savedTapes : this.props.savedTapes();
- // if (savedAnnoMap.size === 0) return undefined;
- // const savedAnnos = Array.from(savedAnnoMap.values())[0];
- const doc = this.props.Document;
- const scale = (this.props.annotationLayerScaling?.() || 1) * NumCast(doc._freeform_scale, 1);
- const marqueeAnno = Docs.Create.FreeformDocument([], {
- onClick: isLinkButton ? FollowLinkScript() : undefined,
- backgroundColor: color,
- annotationOn: this.props.Document,
- title: 'Annotation on ' + this.props.Document.title,
- });
- marqueeAnno.x = NumCast(doc.freeform_panX_min) / scale;
- marqueeAnno.y = NumCast(doc.freeform_panY_min) / scale;
- marqueeAnno._height = parseInt('100') / scale;
- marqueeAnno._width = parseInt('100') / scale;
- return marqueeAnno;
- // }
-
- // const textRegionAnno = Docs.Create.ConfigDocument({
- // annotationOn: this.props.Document,
- // text: this.props.selectionText() as any, // text want an RTFfield, but strings are acceptable, too.
- // text_html: this.props.selectionText() as any,
- // backgroundColor: 'transparent',
- // presentation_duration: 2100,
- // presentation_transition: 500,
- // presentation_zoomText: true,
- // title: '>' + this.props.Document.title,
- // });
- // const textRegionAnnoProto = textRegionAnno[DocData];
- // let minX = Number.MAX_VALUE;
- // let maxX = -Number.MAX_VALUE;
- // let minY = Number.MAX_VALUE;
- // let maxY = -Number.MIN_VALUE;
- // const annoRects: string[] = [];
- // savedAnnoMap.forEach((value: HTMLDivElement[]) =>
- // value.forEach(anno => {
- // const x = parseInt(anno.style.left ?? '0');
- // const y = parseInt(anno.style.top ?? '0');
- // const height = parseInt(anno.style.height ?? '0');
- // const width = parseInt(anno.style.width ?? '0');
- // annoRects.push(`${x}:${y}:${width}:${height}`);
- // anno.remove();
- // minY = Math.min(NumCast(y), minY);
- // minX = Math.min(NumCast(x), minX);
- // maxY = Math.max(NumCast(y) + NumCast(height), maxY);
- // maxX = Math.max(NumCast(x) + NumCast(width), maxX);
- // })
- // );
-
- // textRegionAnnoProto.y = Math.max(minY, 0);
- // textRegionAnnoProto.x = Math.max(minX, 0);
- // textRegionAnnoProto.height = Math.max(maxY, 0) - Math.max(minY, 0);
- // textRegionAnnoProto.width = Math.max(maxX, 0) - Math.max(minX, 0);
- // textRegionAnnoProto.backgroundColor = color;
- // // mainAnnoDocProto.text = this._selectionText;
- // textRegionAnnoProto.text_inlineAnnotations = new List<string>(annoRects);
- // textRegionAnnoProto.opacity = 0;
- // textRegionAnnoProto.layout_unrendered = true;
- // savedAnnoMap.clear();
- // return textRegionAnno;
- };
-
@action
highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => {
// creates annotation documents for current highlights
@@ -272,15 +136,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
return annotationDoc as Doc;
};
- @action
- tape = (color: string, isLinkButton: boolean, savedTapes?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => {
- // creates annotation documents for current highlights
- const effectiveAcl = GetEffectiveAcl(this.props.Document[DocData]);
- const tape = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeTapeDocument(color, isLinkButton, savedTapes);
- addAsAnnotation && tape && this.props.addDocument(tape);
- return tape as Doc;
- };
-
public static previewNewAnnotation = action((savedAnnotations: ObservableMap<number, HTMLDivElement[]>, annotationLayer: HTMLDivElement, div: HTMLDivElement, page: number) => {
div.style.backgroundColor = '#ACCEF7';
div.style.opacity = '0.5';
@@ -327,7 +182,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
AnchorMenu.Instance.OnClick = undoable(() => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true)), 'make sidebar annotation');
AnchorMenu.Instance.OnAudio = unimplementedFunction;
AnchorMenu.Instance.Highlight = (color: string) => this.highlight(color, false, undefined, true);
- AnchorMenu.Instance.Tape = (color: string) => this.tape(color, false, undefined, true);
AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]> /* , addAsAnnotation?: boolean */) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations, true);
AnchorMenu.Instance.onMakeAnchor = () => AnchorMenu.Instance.GetAnchor(undefined, true);
diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss
index 45d9394ed..3ec875df4 100644
--- a/src/client/views/collections/CollectionMenu.scss
+++ b/src/client/views/collections/CollectionMenu.scss
@@ -6,7 +6,7 @@
align-content: center;
justify-content: space-between;
background-color: $dark-gray;
- height: 40px;
+ height: 35px;
border-bottom: $standard-border;
padding: 0 10px;
align-items: center;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8f8cb9083..d611db1f8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,7 +1,7 @@
/* eslint-disable react/jsx-props-no-spreading */
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
-import { Bezier, Point } from 'bezier-js';
+import { Bezier } from 'bezier-js';
import { Colors } from 'browndash-components';
import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
@@ -27,7 +27,6 @@ import { aggregateBounds, clamp, emptyFunction, intersectRect, Utils } from '../
import { Docs } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { DocUtils } from '../../../documents/DocUtils';
-import { FitCurve, GenerateControlPoints } from '../../../util/bezierFit';
import { DragManager } from '../../../util/DragManager';
import { dropActionType } from '../../../util/DropActionTypes';
import { CompileScript } from '../../../util/Scripting';
@@ -56,7 +55,6 @@ import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannable
import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
-import { DrawingOptions, SmartDrawHandler } from '../../smartdraw/SmartDrawHandler';
@observer
class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> {
@@ -120,7 +118,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _panZoomTransition: number = 0; // sets the pan/zoom transform ease time- used by nudge(), focus() etc to smoothly zoom/pan. set to 0 to use document's transition time or default of 0
@observable _firstRender = false; // this turns off rendering of the collection's content so that there's instant feedback when a tab is switched of what content will be shown. could be used for performance improvement
@observable _showAnimTimeline = false;
- @observable _showDrawingEditor = false;
@observable _deleteList: DocumentView[] = [];
@observable _timelineRef = React.createRef<Timeline>();
@observable _marqueeViewRef = React.createRef<MarqueeView>();
@@ -500,30 +497,28 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (!this.Document.isGroup) {
// group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
// prettier-ignore
- const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
switch (Doc.ActiveTool) {
- case InkTool.Highlighter:
- case InkTool.Write:
- case InkTool.Pen:
- break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
+ case InkTool.Highlighter: break;
+ case InkTool.Write: break;
+ case InkTool.Pen: break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
case InkTool.StrokeEraser:
case InkTool.SegmentEraser:
+ this._batch = UndoManager.StartBatch('collectionErase');
+ this._eraserPts.length = 0;
+ setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, emptyFunction);
+ break;
case InkTool.RadiusEraser:
this._batch = UndoManager.StartBatch('collectionErase');
this._eraserPts.length = 0;
- setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, this.onEraserClick, hit !== -1);
- e.stopPropagation();
+ setupMoveUpEvents(this, e, this.onRadiusEraserMove, this.onEraserUp, emptyFunction);
break;
- case InkTool.SmartDraw:
- setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, this.showSmartDraw, hit !== -1);
- e.stopPropagation();
case InkTool.None:
if (!(this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) {
const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, hit !== -1, false);
}
break;
- default:
+ default:
}
}
}
@@ -574,7 +569,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
};
-
@action
onEraserUp = (): void => {
this._deleteList.lastElement()?._props.removeDocument?.(this._deleteList.map(ink => ink.Document));
@@ -615,48 +609,34 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
_eraserLock = 0;
_eraserPts: number[][] = []; // keep track of the last few eraserPts to make the eraser circle 'stretch'
- erase = (e: PointerEvent, delta: number[]) => {
- e.stopImmediatePropagation();
+ /**
+ * Erases strokes by intersecting them with an invisible "eraser stroke".
+ * By default this iterates through all intersected ink strokes, determines their segmentation, draws back the non-intersected segments,
+ * and deletes the original stroke.
+ */
+ @action
+ onEraserMove = (e: PointerEvent, down: number[], delta: number[]) => {
const currPoint = { X: e.clientX, Y: e.clientY };
this._eraserPts.push([currPoint.X, currPoint.Y]);
this._eraserPts = this._eraserPts.slice(Math.max(0, this._eraserPts.length - 5));
- if (Doc.ActiveTool === InkTool.RadiusEraser) {
- const strokeMap: Map<DocumentView, number[]> = this.getRadiusEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint);
- strokeMap.forEach((intersects, stroke) => {
- if (!this._deleteList.includes(stroke)) {
- this._deleteList.push(stroke);
- SetActiveInkWidth(StrCast(stroke.Document.stroke_width?.toString()) || '1');
- SetActiveInkColor(StrCast(stroke.Document.color?.toString()) || 'black');
- const segments = this.radiusErase(stroke, intersects.sort());
- segments?.forEach(segment =>
- this.forceStrokeGesture(
- e,
- Gestures.Stroke,
- segment.reduce((data, curve) => [...data, ...curve.points.map(p => stroke.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[])
- )
- );
- }
- stroke.layoutDoc.opacity = 0;
- stroke.layoutDoc.dontIntersect = true;
- });
- } else {
- this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => {
- if (!this._deleteList.includes(intersect.inkView)) {
- this._deleteList.push(intersect.inkView);
- SetActiveInkWidth(StrCast(intersect.inkView.Document.stroke_width?.toString()) || '1');
- SetActiveInkColor(StrCast(intersect.inkView.Document.color?.toString()) || 'black');
- // create a new curve by appending all curves of the current segment together in order to render a single new stroke.
- if (Doc.ActiveTool !== InkTool.StrokeEraser) {
- // this._eraserLock++;
- const segments = this.segmentErase(intersect.inkView, intersect.t); // intersect.t is where the eraser intersected the ink stroke - want to remove the segment that starts at the intersection just before this t value and goes to the one just after it
- const newStrokes = segments?.map(segment => {
- const points = segment.reduce((data, curve) => [...data, ...curve.points.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]);
- const bounds = InkField.getBounds(points);
- const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
- const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
- return Docs.Create.InkDocument(
- points,
- { title: 'stroke',
+ // if (this._eraserLock) return false; // leaving this commented out in case the idea is revisited in the future
+ this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => {
+ if (!this._deleteList.includes(intersect.inkView)) {
+ this._deleteList.push(intersect.inkView);
+ SetActiveInkWidth(StrCast(intersect.inkView.Document.stroke_width?.toString()) || '1');
+ SetActiveInkColor(StrCast(intersect.inkView.Document.color?.toString()) || 'black');
+ // create a new curve by appending all curves of the current segment together in order to render a single new stroke.
+ if (Doc.ActiveTool !== InkTool.StrokeEraser) {
+ // this._eraserLock++;
+ const segments = this.segmentErase(intersect.inkView, intersect.t); // intersect.t is where the eraser intersected the ink stroke - want to remove the segment that starts at the intersection just before this t value and goes to the one just after it
+ const newStrokes = segments?.map(segment => {
+ const points = segment.reduce((data, curve) => [...data, ...curve.points.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]);
+ const bounds = InkField.getBounds(points);
+ const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
+ const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
+ return Docs.Create.InkDocument(
+ points,
+ { title: 'stroke',
x: B.x - inkWidth / 2,
y: B.y - inkWidth / 2,
_width: B.width + inkWidth,
@@ -675,30 +655,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
newStrokes && this.addDocument?.(newStrokes);
// setTimeout(() => this._eraserLock--);
}
- });
- }
- return false;
- };
-
- /**
- * Erases strokes by intersecting them with an invisible "eraser stroke".
- * By default this iterates through all intersected ink strokes, determines their segmentation, draws back the non-intersected segments,
- * and deletes the original stroke.
- */
- @action
- onEraserMove = (e: PointerEvent, down: number[], delta: number[]) => {
- this.erase(e, delta);
- // if (this._eraserLock) return false; // leaving this commented out in case the idea is revisited in the future
+ // Lower ink opacity to give the user a visual indicator of deletion.
+ intersect.inkView.layoutDoc.opacity = 0;
+ intersect.inkView.layoutDoc.dontIntersect = true;
+ }
+ });
return false;
};
- @action
- onEraserClick = (e: PointerEvent, doubleTap?: boolean) => {
- e.preventDefault();
- e.stopImmediatePropagation();
- this.erase(e, [0, 0]);
- };
-
/**
* Erases strokes by intersecting them with a circle of variable radius. Essentially creates an InkField for the
* eraser circle, then determines its intersections with other ink strokes. Each stroke's DocumentView and its
@@ -708,32 +672,32 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
* @param delta
* @returns
*/
- // @action
- // onRadiusEraserMove = (e: PointerEvent, down: number[], delta: number[]) => {
- // const currPoint = { X: e.clientX, Y: e.clientY };
- // this._eraserPts.push([currPoint.X, currPoint.Y]);
- // this._eraserPts = this._eraserPts.slice(Math.max(0, this._eraserPts.length - 5));
- // const strokeMap: Map<DocumentView, number[]> = this.getRadiusEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint);
-
- // strokeMap.forEach((intersects, stroke) => {
- // if (!this._deleteList.includes(stroke)) {
- // this._deleteList.push(stroke);
- // SetActiveInkWidth(StrCast(stroke.Document.stroke_width?.toString()) || '1');
- // SetActiveInkColor(StrCast(stroke.Document.color?.toString()) || 'black');
- // const segments = this.radiusErase(stroke, intersects.sort());
- // segments?.forEach(segment =>
- // this.forceStrokeGesture(
- // e,
- // Gestures.Stroke,
- // segment.reduce((data, curve) => [...data, ...curve.points.map(p => stroke.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[])
- // )
- // );
- // }
- // stroke.layoutDoc.opacity = 0;
- // stroke.layoutDoc.dontIntersect = true;
- // });
- // return false;
- // };
+ @action
+ onRadiusEraserMove = (e: PointerEvent, down: number[], delta: number[]) => {
+ const currPoint = { X: e.clientX, Y: e.clientY };
+ this._eraserPts.push([currPoint.X, currPoint.Y]);
+ this._eraserPts = this._eraserPts.slice(Math.max(0, this._eraserPts.length - 5));
+ const strokeMap: Map<DocumentView, number[]> = this.getRadiusEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint);
+
+ strokeMap.forEach((intersects, stroke) => {
+ if (!this._deleteList.includes(stroke)) {
+ this._deleteList.push(stroke);
+ SetActiveInkWidth(StrCast(stroke.Document.stroke_width?.toString()) || '1');
+ SetActiveInkColor(StrCast(stroke.Document.color?.toString()) || 'black');
+ const segments = this.radiusErase(stroke, intersects.sort());
+ segments?.forEach(segment =>
+ this.forceStrokeGesture(
+ e,
+ Gestures.Stroke,
+ segment.reduce((data, curve) => [...data, ...curve.points.map(p => stroke.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[])
+ )
+ );
+ }
+ stroke.layoutDoc.opacity = 0;
+ stroke.layoutDoc.dontIntersect = true;
+ });
+ return false;
+ };
forceStrokeGesture = (e: PointerEvent, gesture: Gestures, points: InkData, text?: any) => {
this.onGesture(e, new GestureUtils.GestureEvent(gesture, points, InkField.getBounds(points), text));
@@ -764,7 +728,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// increase radius slightly based on the erased stroke's width, added to make eraser look more realistic
const radius = ActiveEraserWidth() + 5 + inkStrokeWidth * 0.1; // add 5 to prevent eraser from being too small
const c = 0.551915024494; // circle tangent length to side ratio
- const movement = { x: Math.max(endInkCoordsIn.X - startInkCoordsIn.X, 1), y: Math.max(endInkCoordsIn.Y - startInkCoordsIn.Y, 1) };
+ const movement = { x: endInkCoordsIn.X - startInkCoordsIn.X, y: endInkCoordsIn.Y - startInkCoordsIn.Y };
const moveLen = Math.sqrt(movement.x ** 2 + movement.y ** 2);
const direction = { x: (movement.x / moveLen) * radius, y: (movement.y / moveLen) * radius };
const normal = { x: -direction.y, y: direction.x }; // prettier-ignore
@@ -1275,60 +1239,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@action
- showSmartDraw = (e: PointerEvent, doubleTap?: boolean) => {
- SmartDrawHandler.Instance.displaySmartDrawHandler(e.pageX, e.pageY, this.createDrawing, this.removeDrawing);
- };
-
- _drawing: Doc[] = [];
- @undoBatch
- createDrawing = (e: React.PointerEvent<Element>, strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => {
- strokeData.forEach((stroke: [InkData, string, string]) => {
- const bounds = InkField.getBounds(stroke[0]);
- const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
- const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
- const inkDoc = Docs.Create.InkDocument(
- stroke[0],
- { title: 'stroke',
- x: B.x - inkWidth / 2,
- y: B.y - inkWidth / 2,
- _width: B.width + inkWidth,
- _height: B.height + inkWidth,
- stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
- inkWidth,
- stroke[1],
- undefined,
- stroke[2] === 'none' ? undefined : stroke[2]
- );
- this._drawing.push(inkDoc);
- this.addDocument(inkDoc);
- });
- const collection = this._marqueeViewRef.current?.collection(undefined, true, this._drawing);
- if (collection) {
- const docData = collection[DocData];
- docData.title = opts.text;
- docData.drawingInput = opts.text;
- docData.drawingComplexity = opts.complexity;
- docData.drawingColored = opts.autoColor;
- docData.drawingSize = opts.size;
- docData.drawingData = gptRes;
- }
- this._batch?.end();
- };
-
- removeDrawing = (doc?: Doc) => {
- this._batch = UndoManager.StartBatch('regenerateDrawing');
- if (doc) {
- const docData: Doc = doc[DocData];
- const children = docData.data as unknown as Doc[];
- this._props.removeDocument?.(doc);
- this._props.removeDocument?.(children);
- } else {
- this._props.removeDocument?.(this._drawing);
- }
- this._drawing = [];
- };
-
- @action
zoom = (pointX: number, pointY: number, deltaY: number): void => {
if (this.Document.isGroup || this.Document[(this._props.viewField ?? '_') + 'freeform_noZoom']) return;
let deltaScale = deltaY > 0 ? 1 / 1.05 : 1.05;
@@ -1921,10 +1831,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
onCursorMove = (e: React.PointerEvent) => {
- const locPt = this.ScreenToLocalBoxXf().transformPoint(e.clientX, e.clientY);
- this._eraserX = locPt[0];
- this._eraserY = locPt[1];
- // Doc.ActiveTool === InkTool.RadiusEraser ? this._childPointerEvents = 'none' : this._childPointerEvents = 'all'
+ this._eraserX = e.clientX;
+ this._eraserY = e.clientY;
// super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
};
@@ -2031,14 +1939,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}),
icon: 'eye',
});
- optionItems.push({
- description: (this._showDrawingEditor ? 'Close' : 'Show') + ' Drawing Editor',
- event: action(() => {
- this._showDrawingEditor = !this._showDrawingEditor;
- this._showDrawingEditor ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10, this.createDrawing, this.removeDrawing) : SmartDrawHandler.Instance.hideRegenerate();
- }),
- icon: 'pen-to-square',
- });
this._props.renderDepth &&
optionItems.push({
description: 'Use Background Color as Default',
@@ -2243,8 +2143,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onPointerMove={this.onCursorMove}
style={{
position: 'fixed',
- left: this._eraserX,
- top: this._eraserY,
+ left: this._eraserX - 60,
+ top: this._eraserY - 100,
width: (ActiveEraserWidth() + 5) * 2,
height: (ActiveEraserWidth() + 5) * 2,
borderRadius: '50%',
diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss b/src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss
index 9b8727e1a..e7413bf8e 100644
--- a/src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss
+++ b/src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss
@@ -42,17 +42,3 @@
}
}
}
-
-.complexity-slider {
- width: 50%; /* Full-width */
- height: 25px; /* Specified height */
- background: #d3d3d3; /* Grey background */
- outline: none; /* Remove outline */
- opacity: 0.7; /* Set transparency (for mouse-over effects on hover) */
- -webkit-transition: 0.2s; /* 0.2 seconds transition on hover */
- transition: opacity 0.2s;
-
- :hover {
- opacity: 1; /* Fully shown on mouse-over */
- }
-}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 5aff3ed6f..dc15c83c5 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -36,7 +36,6 @@ import { CollectionFreeFormView } from './CollectionFreeFormView';
import { ImageLabelHandler } from './ImageLabelHandler';
import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
import './MarqueeView.scss';
-import { collectionOf } from '@turf/turf';
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -375,8 +374,8 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
return doc;
})(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
newCollection.isSystem = undefined;
- newCollection._width = this.Bounds.width || 1; // if width/height are unset/0, then groups won't autoexpand to contain their children
- newCollection._height = this.Bounds.height || 1;
+ newCollection._width = this.Bounds.width;
+ newCollection._height = this.Bounds.height;
newCollection._dragWhenActive = makeGroup;
newCollection.x = this.Bounds.left;
newCollection.y = this.Bounds.top;
@@ -427,7 +426,6 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
this._props.selectDocuments([newCollection]);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
- return newCollection;
});
/**
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index d46e72d75..2b7de5082 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -506,9 +506,9 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'labels' | 'fil
setMode: () => { SetActiveInkColor(StrCast(value)); selected?.type === DocumentType.INK && setActiveTool(GestureOverlay.Instance.InkShape ?? InkTool.Pen, true, false);},
}],
[ 'eraserWidth', {
- checkResult: () => ActiveEraserWidth() === 0 ? 1 : ActiveEraserWidth(),
+ checkResult: () => ActiveEraserWidth(),
setInk: (doc: Doc) => { },
- setMode: () => { SetEraserWidth(value);},
+ setMode: () => { SetEraserWidth(value.toString());},
}]
]);
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 6e24b2931..7bca1230f 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -20,27 +20,15 @@
top: 0;
left: 0;
- .pdfBox-sidebarBtn-container {
- display: flex;
- flex-direction: row;
- position: absolute;
- width: 53px;
- height: 33px;
- right: 5px;
- align-items: center;
- justify-content: space-between;
- z-index: 1;
- }
-
// glr: This should really be the same component as text and PDFs
.pdfBox-sidebarBtn {
background: $black;
height: 25px;
width: 25px;
- // right: 5px;
+ right: 5px;
color: $white;
display: flex;
- // position: absolute;
+ position: absolute;
align-items: center;
justify-content: center;
border-radius: 3px;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 782df99f6..8db68ddfe 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,8 +1,6 @@
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable jsx-a11y/control-has-associated-label */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { IconButton } from 'browndash-components';
-import { black } from 'colors';
import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as Pdfjs from 'pdfjs-dist';
@@ -505,30 +503,17 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}
@computed get sidebarHandle() {
return (
- <div className="pdfBox-sidebarBtn-container">
- <div
- className="pdfBox-sidebarBtn"
- key="sidebar"
- title="Toggle Sidebar"
- style={{
- display: !this._props.isContentActive() ? 'none' : undefined,
- top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5,
- backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
- }}>
- {/* // onPointerDown={e => this.sidebarBtnDown(e, true)} */}
- <IconButton tooltip="Toggle Annotation Palette" icon={<FontAwesomeIcon style={{ color: Colors.WHITE }} icon="palette" />} onPointerDown={e => this.sidebarBtnDown(e, true)} />
- </div>
- <div
- className="pdfBox-sidebarBtn"
- key="sidebar"
- title="Toggle Sidebar"
- style={{
- display: !this._props.isContentActive() ? 'none' : undefined,
- top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5,
- backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
- }}>
- <IconButton tooltip="Toggle Sidebar" icon={<FontAwesomeIcon style={{ color: Colors.WHITE }} icon="comment-alt" size="sm" />} onPointerDown={e => this.sidebarBtnDown(e, true)} />
- </div>
+ <div
+ className="pdfBox-sidebarBtn"
+ key="sidebar"
+ title="Toggle Sidebar"
+ style={{
+ display: !this._props.isContentActive() ? 'none' : undefined,
+ top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5,
+ backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
+ }}
+ onPointerDown={e => this.sidebarBtnDown(e, true)}>
+ <FontAwesomeIcon style={{ color: Colors.WHITE }} icon="comment-alt" size="sm" />
</div>
);
}
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index df990b0c0..2f6824466 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -51,7 +51,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
public Highlight: (color: string) => Opt<Doc> = (/* color: string */) => undefined;
- public Tape: (color: string) => Opt<Doc> = (/* color: string */) => undefined;
public GetAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = emptyFunction;
public Delete: () => void = unimplementedFunction;
public PinToPres: () => void = unimplementedFunction;
@@ -173,12 +172,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
AnchorMenu.Instance.fadeOut(true);
};
- @action
- tapeClicked = () => {
- this.Tape(this.highlightColor);
- // AnchorMenu.Instance.fadeOut(true);
- };
-
@computed get highlighter() {
return (
<Group>
@@ -189,13 +182,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
colorPicker={this.highlightColor}
color={SettingsManager.userColor}
/>
- <IconButton
- tooltip="Click to Add Tape" //
- icon={<FontAwesomeIcon icon="tape" />}
- onClick={this.tapeClicked}
- colorPicker={this.highlightColor}
- color={SettingsManager.userColor}
- />
<ColorPicker selectedColor={this.highlightColor} setFinalColor={this.changeHighlightColor} setSelectedColor={this.changeHighlightColor} size={Size.XSMALL} />
</Group>
);
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index fbe3518ec..db47a84e1 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -65,7 +65,6 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
@observable _pageSizes: { width: number; height: number }[] = [];
@observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
- @observable _savedTapes = new ObservableMap<number, HTMLDivElement[]>();
@observable _textSelecting = true;
@observable _showWaiting = true;
@observable Index: number = -1;
@@ -582,7 +581,6 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
return <div className={'pdfViewerDash-text' + (this._props.pointerEvents?.() !== 'none' && this._textSelecting && this._props.isContentActive() ? '-selected' : '')} ref={this._viewer} />;
}
savedAnnotations = () => this._savedAnnotations;
- savedTapes = () => this._savedTapes;
addDocumentWrapper = (doc: Doc | Doc[]) => this._props.addDocument!(doc);
render() {
TraceMobx();
@@ -616,7 +614,6 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
docView={this._props.pdfBox.DocumentView!}
finishMarquee={this.finishMarquee}
savedAnnotations={this.savedAnnotations}
- savedTapes={this.savedTapes}
selectionText={this.selectionText}
annotationLayer={this._annotationLayer.current}
marqueeContainer={this._mainCont.current}
diff --git a/src/client/views/smartdraw/DrawingPalette.scss b/src/client/views/smartdraw/DrawingPalette.scss
deleted file mode 100644
index 0f1152b71..000000000
--- a/src/client/views/smartdraw/DrawingPalette.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-.drawing-palette {
- display: grid;
- grid-template-columns: auto;
- position: absolute;
- right: 14px;
- width: 170px;
- height: 170px;
- top: 50px;
- border-radius: 5px;
- background-color: white;
-}
diff --git a/src/client/views/smartdraw/DrawingPalette.tsx b/src/client/views/smartdraw/DrawingPalette.tsx
deleted file mode 100644
index 87a39bc85..000000000
--- a/src/client/views/smartdraw/DrawingPalette.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-import { computed, makeObservable, observable } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { returnAll, returnFalse, returnOne, returnZero } from '../../../ClientUtils';
-import { Doc, StrListCast } from '../../../fields/Doc';
-import { emptyFunction } from '../../../Utils';
-import { CollectionViewType } from '../../documents/DocumentTypes';
-import { MarqueeView } from '../collections/collectionFreeForm';
-import { CollectionGridView } from '../collections/collectionGrid';
-import { CollectionStackingView } from '../collections/CollectionStackingView';
-import { DocumentView } from '../nodes/DocumentView';
-import { FieldViewProps } from '../nodes/FieldView';
-import { ObservableReactComponent } from '../ObservableReactComponent';
-import './DrawingPalette.scss';
-
-@observer
-export class DrawingPalette extends ObservableReactComponent<{}> {
- @observable private _savedDrawings: Doc[] = [];
- @observable _marqueeViewRef = React.createRef<MarqueeView>();
- private _stackRef = React.createRef<CollectionStackingView>();
-
- constructor(props: any) {
- super(props);
- makeObservable(this);
- }
-
- panelWidth = () => 100;
- panelHeight = () => 100;
-
- getCollection = () => {
- return this._marqueeViewRef.current?.collection(undefined, false, this._savedDrawings) || new Doc();
- };
-
- @computed get savedDrawingAnnos() {
- // const savedAnnos = Doc.MyDrawingAnnos;
- return (
- <div className="collectionMenu-contMenuButtons" style={{ height: '100%' }}>
- {/* <DocumentView PanelHeight={this.panelWidth} PanelWidth={this.panelHeight} Document={savedAnnos} renderDepth={2} isContentActive={returnFalse} childFilters={this.childFilters} /> */}
- {/* <CollectionStackingView
- {...this._props}
- Document={savedAnnos}
- // setContentViewBox={emptyFunction}
- // NativeWidth={returnZero}
- // NativeHeight={returnZero}
- ref={this._stackRef}
- PanelHeight={this.panelWidth}
- PanelWidth={this.panelHeight}
- // childFilters={this.childFilters}
- // sortFunc={this.sortByLinkAnchorY}
- // setHeight={this.setHeightCallback}
- // isAnnotationOverlay={false}
- // select={emptyFunction}
- NativeDimScaling={returnOne}
- // childlayout_showTitle={this.layout_showTitle}
- isContentActive={returnFalse}
- isSelected={returnFalse}
- isAnyChildContentActive={returnFalse}
- // childDocumentsActive={this._props.isContentActive}
- whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
- childHideDecorationTitle
- // ScreenToLocalTransform={this.screenToLocalTransform}
- renderDepth={this._props.renderDepth + 1}
- type_collection={CollectionViewType.Stacking}
- // fieldKey={'drawing-palette'}
- pointerEvents={returnAll}
- /> */}
- </div>
- );
- }
-
- render() {
- return (
- <div className="drawing-palette">
- {/* {this._savedDrawings.map(doc => {
- return <DocumentView
- Document={doc}
- renderDepth={0}
- PanelWidth={this.panelWidth}
- PanelHeight={this.panelHeight}
- isContentActive={this.isContentActive} />;
- })} */}
- {/* <CollectionGridView {...this._props} /> */}
- {}
- {/* <DocumentView Document={this.getCollection()} /> */}
- {this.savedDrawingAnnos}
- </div>
- );
- }
-}
diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx
deleted file mode 100644
index d24cc9d50..000000000
--- a/src/client/views/smartdraw/SmartDrawHandler.tsx
+++ /dev/null
@@ -1,409 +0,0 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, makeObservable, observable } from 'mobx';
-import { observer } from 'mobx-react';
-import React from 'react';
-import { SettingsManager } from '../../util/SettingsManager';
-import { ObservableReactComponent } from '../ObservableReactComponent';
-import { Button, IconButton } from 'browndash-components';
-import ReactLoading from 'react-loading';
-import { AiOutlineSend } from 'react-icons/ai';
-// import './ImageLabelHandler.scss';
-import { gptAPICall, GPTCallType } from '../../apis/gpt/GPT';
-import { InkData } from '../../../fields/InkField';
-import { SVGToBezier } from '../../util/bezierFit';
-const { parse } = require('svgson');
-import { Slider, Switch } from '@mui/material';
-import { Doc } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
-import { DocumentView } from '../nodes/DocumentView';
-
-export interface DrawingOptions {
- text: string;
- complexity: number;
- size: number;
- autoColor: boolean;
- x: number;
- y: number;
-}
-
-@observer
-export class SmartDrawHandler extends ObservableReactComponent<{}> {
- static Instance: SmartDrawHandler;
-
- @observable private _display: boolean = false;
- @observable private _pageX: number = 0;
- @observable private _pageY: number = 0;
- @observable private _yRelativeToTop: boolean = true;
- @observable private _isLoading: boolean = false;
- @observable private _userInput: string = '';
- @observable private _showOptions: boolean = false;
- @observable private _showEditBox: boolean = false;
- @observable private _showRegenerate: boolean = false;
- @observable private _complexity: number = 5;
- @observable private _size: number = 200;
- @observable private _autoColor: boolean = true;
- @observable private _regenInput: string = '';
- private _addFunc: (e: React.PointerEvent<Element>, strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void = () => {};
- private _deleteFunc: (doc?: Doc) => void = () => {};
- private _lastInput: DrawingOptions = { text: '', complexity: 5, size: 300, autoColor: true, x: 0, y: 0 };
- private _lastResponse: string = '';
- private _selectedDoc: Doc | undefined = undefined;
-
- constructor(props: any) {
- super(props);
- makeObservable(this);
- SmartDrawHandler.Instance = this;
- }
-
- @action
- setUserInput = (input: string) => {
- this._userInput = input;
- };
-
- @action
- setRegenInput = (input: string) => {
- this._regenInput = input;
- };
-
- @action
- setShowOptions = () => {
- this._showOptions = !this._showOptions;
- };
-
- @action
- setComplexity = (val: number) => {
- this._complexity = val;
- };
-
- @action
- setSize = (val: number) => {
- this._size = val;
- };
-
- @action
- setAutoColor = () => {
- this._autoColor = !this._autoColor;
- };
-
- @action
- displaySmartDrawHandler = (x: number, y: number, addFunc: (e: React.PointerEvent<Element>, strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void, deleteFunc: (doc?: Doc) => void) => {
- this._pageX = x;
- this._pageY = y;
- this._display = true;
- this._addFunc = addFunc;
- this._deleteFunc = deleteFunc;
- };
-
- @action
- displayRegenerate = (x: number, y: number, addFunc: (e: React.PointerEvent<Element>, strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void, deleteFunc: (doc?: Doc) => void) => {
- this._selectedDoc = DocumentView.SelectedDocs().lastElement();
- const docData = this._selectedDoc[DocData];
- this._addFunc = addFunc;
- this._deleteFunc = deleteFunc;
- this._pageX = x;
- this._pageY = y;
- this._showRegenerate = true;
- this._lastResponse = docData.drawingData as string;
- this._lastInput = { text: docData.drawingInput as string, complexity: docData.drawingComplexity as number, size: docData.drawingSize as number, autoColor: docData.drawingColored as boolean, x: this._pageX, y: this._pageY };
- };
-
- @action
- hideSmartDrawHandler = () => {
- this._showRegenerate = false;
- this._display = false;
- this._isLoading = false;
- this._showOptions = false;
- this._userInput = '';
- this._complexity = 5;
- this._size = 300;
- this._autoColor = true;
- // this._regenInput = ''
- };
-
- @action
- hideRegenerate = () => {
- this._showRegenerate = false;
- this._isLoading = false;
- this._regenInput = '';
- };
-
- _errorOccurredOnce = false;
- @action
- drawWithGPT = async (e: React.PointerEvent<Element>, input: string) => {
- if (input === '') return;
- this._lastInput = { text: input, complexity: this._complexity, size: this._size, autoColor: this._autoColor, x: e.clientX, y: e.clientY };
- this._isLoading = true;
- this._showOptions = false;
- try {
- const res = await gptAPICall(`"${input}", "${this._complexity}", "${this._size}"`, GPTCallType.DRAW, undefined, true);
- if (!res) {
- console.error('GPT call failed');
- return;
- }
- console.log(res);
- await this.parseResponse(e, res, { X: e.clientX, Y: e.clientY }, false);
- this.hideSmartDrawHandler();
- this._showRegenerate = true;
- this._errorOccurredOnce = false;
- } catch (err) {
- if (this._errorOccurredOnce) {
- console.error('GPT call failed', err);
- this._errorOccurredOnce = false;
- } else {
- this._errorOccurredOnce = true;
- this.drawWithGPT(e, input);
- }
- }
- this._isLoading = false;
- };
-
- @action
- edit = () => {
- this._showEditBox = !this._showEditBox;
- };
-
- @action
- regenerate = async (e: React.PointerEvent<Element>) => {
- this._isLoading = true;
- try {
- let res;
- if (this._regenInput !== '') {
- const prompt: string = `This is your previously generated svg code: ${this._lastResponse} for the user input "${this._lastInput.text}". Please regenerate it with the provided specifications.`;
- res = await gptAPICall(`"${this._regenInput}"`, GPTCallType.DRAW, prompt, true);
- this._lastInput.text = `${this._lastInput.text} + ${this._regenInput}`;
- } else {
- res = await gptAPICall(`"${this._lastInput.text}", "${this._lastInput.complexity}", "${this._lastInput.size}"`, GPTCallType.DRAW, undefined, true);
- }
- if (!res) {
- console.error('GPT call failed');
- return;
- }
- console.log(res);
- this.parseResponse(e, res, { X: this._lastInput.x, Y: this._lastInput.y }, true);
- } catch (err) {
- console.error('GPT call failed', err);
- }
- this._isLoading = false;
- this._regenInput = '';
- this._showEditBox = false;
- };
-
- @action
- parseResponse = async (e: React.PointerEvent<Element>, res: string, startPoint: { X: number; Y: number }, regenerate: boolean) => {
- const svg = res.match(/<svg[^>]*>([\s\S]*?)<\/svg>/g);
- console.log('start point is', startPoint);
- if (svg) {
- this._lastResponse = svg[0];
- const svgObject = await parse(svg[0]);
- const svgStrokes: any = svgObject.children;
- const strokeData: [InkData, string, string][] = [];
- console.log('autocolor is', this._autoColor);
- svgStrokes.forEach((child: any) => {
- const convertedBezier: InkData = SVGToBezier(child.name, child.attributes);
- strokeData.push([
- convertedBezier.map(point => {
- return { X: point.X + startPoint.X - this._size / 1.5, Y: point.Y + startPoint.Y - this._size / 2 };
- }),
- (regenerate ? this._lastInput.autoColor : this._autoColor) ? child.attributes.stroke : undefined,
- (regenerate ? this._lastInput.autoColor : this._autoColor) ? child.attributes.fill : undefined,
- ]);
- });
- if (regenerate) {
- this._deleteFunc(this._selectedDoc);
- this._addFunc(e, strokeData, this._lastInput, svg[0], this._selectedDoc);
- } else {
- this._addFunc(e, strokeData, this._lastInput, svg[0]);
- }
- }
- };
-
- render() {
- if (this._display) {
- return (
- <div
- id="label-handler"
- className="contextMenu-cont"
- style={{
- display: this._display ? '' : 'none',
- left: this._pageX,
- ...(this._yRelativeToTop ? { top: Math.max(0, this._pageY) } : { bottom: this._pageY }),
- background: SettingsManager.userBackgroundColor,
- color: SettingsManager.userColor,
- }}>
- <div>
- <IconButton
- tooltip={'Cancel'}
- onClick={() => {
- this.hideSmartDrawHandler();
- this.hideRegenerate();
- }}
- icon={<FontAwesomeIcon icon="xmark" />}
- color={SettingsManager.userColor}
- style={{ width: '19px' }}
- />
- <input
- aria-label="label-input"
- id="new-label"
- type="text"
- style={{ color: 'black' }}
- value={this._userInput}
- onChange={e => {
- this.setUserInput(e.target.value);
- }}
- placeholder="Enter item to draw"
- />
- <Button
- style={{ alignSelf: 'flex-end' }}
- text="Send"
- icon={this._isLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />}
- iconPlacement="right"
- color={SettingsManager.userColor}
- onClick={e => {
- this.drawWithGPT(e as React.PointerEvent<Element>, this._userInput);
- }}
- />
- </div>
- {this._showOptions && (
- <>
- <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-around' }}>
- <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', width: '30%' }}>
- Auto color
- <Switch
- sx={{
- '& .MuiSwitch-switchBase.Mui-checked': {
- color: SettingsManager.userColor,
- },
- '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': {
- backgroundColor: SettingsManager.userVariantColor,
- },
- }}
- defaultChecked={true}
- size="small"
- onChange={this.setAutoColor}
- />
- </div>
- <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', width: '31%' }}>
- Complexity
- <Slider
- sx={{
- '& .MuiSlider-thumb': {
- color: SettingsManager.userColor,
- '&.Mui-focusVisible, &:hover, &.Mui-active': {
- boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}10`,
- },
- },
- '& .MuiSlider-track': {
- color: SettingsManager.userVariantColor,
- },
- '& .MuiSlider-rail': {
- color: SettingsManager.userColor,
- },
- }}
- style={{ width: '80%' }}
- min={1}
- max={10}
- step={1}
- size="small"
- value={this._complexity}
- onChange={(e, val) => {
- this.setComplexity(val as number);
- }}
- valueLabelDisplay="auto"
- />
- </div>
- <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', width: '39%' }}>
- Size (in pixels)
- <Slider
- sx={{
- '& .MuiSlider-thumb': {
- color: SettingsManager.userColor,
- '&.Mui-focusVisible, &:hover, &.Mui-active': {
- boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}20`,
- },
- },
- '& .MuiSlider-track': {
- color: SettingsManager.userVariantColor,
- },
- '& .MuiSlider-rail': {
- color: SettingsManager.userColor,
- },
- }}
- style={{ width: '80%' }}
- min={50}
- max={700}
- step={10}
- size="small"
- value={this._size}
- onChange={(e, val) => {
- this.setSize(val as number);
- }}
- valueLabelDisplay="auto"
- />
- </div>
- </div>
- </>
- )}
- </div>
- );
- } else if (this._showRegenerate) {
- return (
- <div
- id="smartdraw-options-menu"
- className="contextMenu-cont"
- style={{
- left: this._pageX,
- ...(this._yRelativeToTop ? { top: Math.max(0, this._pageY) } : { bottom: this._pageY }),
- background: SettingsManager.userBackgroundColor,
- color: SettingsManager.userColor,
- }}>
- <div
- style={{
- display: 'flex',
- flexDirection: 'row',
- }}>
- <IconButton
- tooltip="Regenerate"
- icon={this._isLoading && this._regenInput === '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <FontAwesomeIcon icon={'rotate'} />}
- color={SettingsManager.userColor}
- onClick={e => {
- this.regenerate(e as React.PointerEvent<Element>);
- }}
- />
- <IconButton tooltip="Edit with GPT" icon={<FontAwesomeIcon icon="pen-to-square" />} color={SettingsManager.userColor} onClick={this.edit} />
- {this._showEditBox && (
- <div
- style={{
- display: 'flex',
- flexDirection: 'row',
- }}>
- <input
- aria-label="Edit instructions input"
- id="regen-input"
- type="text"
- style={{ color: 'black' }}
- value={this._regenInput}
- onChange={e => {
- this.setRegenInput(e.target.value);
- }}
- placeholder="Edit instructions"
- />
- <Button
- style={{ alignSelf: 'flex-end' }}
- text="Send"
- icon={this._isLoading && this._regenInput !== '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />}
- iconPlacement="right"
- color={SettingsManager.userColor}
- onClick={e => {
- this.regenerate(e as React.PointerEvent<Element>);
- }}
- />
- </div>
- )}
- </div>
- </div>
- );
- } else {
- return <></>;
- }
- }
-}