diff options
| author | eleanor-park <eleanor_park@brown.edu> | 2024-07-11 12:08:26 -0400 |
|---|---|---|
| committer | eleanor-park <eleanor_park@brown.edu> | 2024-07-11 12:08:26 -0400 |
| commit | b88f3a79b4558b222864b7c925fd0d5086cdcea2 (patch) | |
| tree | 4f830b4a4dd96948983115e1e5bff07a1a6eb3d1 /src/client/views | |
| parent | 4438e7fe202ff4091b26f073122e7866ec9abb46 (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.tsx | 28 | ||||
| -rw-r--r-- | src/client/views/LightboxView.scss | 24 | ||||
| -rw-r--r-- | src/client/views/LightboxView.tsx | 7 | ||||
| -rw-r--r-- | src/client/views/MainView.tsx | 5 | ||||
| -rw-r--r-- | src/client/views/MarqueeAnnotator.tsx | 146 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionMenu.scss | 2 | ||||
| -rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 242 | ||||
| -rw-r--r-- | src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss | 14 | ||||
| -rw-r--r-- | src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 6 | ||||
| -rw-r--r-- | src/client/views/global/globalScripts.ts | 4 | ||||
| -rw-r--r-- | src/client/views/nodes/PDFBox.scss | 16 | ||||
| -rw-r--r-- | src/client/views/nodes/PDFBox.tsx | 37 | ||||
| -rw-r--r-- | src/client/views/pdf/AnchorMenu.tsx | 14 | ||||
| -rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 3 | ||||
| -rw-r--r-- | src/client/views/smartdraw/DrawingPalette.scss | 11 | ||||
| -rw-r--r-- | src/client/views/smartdraw/DrawingPalette.tsx | 89 | ||||
| -rw-r--r-- | src/client/views/smartdraw/SmartDrawHandler.tsx | 409 |
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 <></>; - } - } -} |
