From 647b66c965f5896d784de0f321d31cc712937e1c Mon Sep 17 00:00:00 2001 From: eleanor-park Date: Fri, 26 Jul 2024 01:23:06 -0400 Subject: Revert "Merge branch 'master' into eleanor-gptdraw" This reverts commit decbefe23a1da35c838222bafe8a2c029c6ea794, reversing changes made to 8ca26551622d36b7856f5c1865498fa9e5d888b5. --- src/client/views/pdf/PDFViewer.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index fa5e5cedb..fbe3518ec 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -33,7 +33,7 @@ import './PDFViewer.scss'; // pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`; // The workerSrc property shall be specified. -Pdfjs.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@4.4.168/build/pdf.worker.mjs'; +Pdfjs.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@4.3.136/build/pdf.worker.mjs'; interface IViewerProps extends FieldViewProps { pdfBox: PDFBox; @@ -56,7 +56,7 @@ interface IViewerProps extends FieldViewProps { */ @observer export class PDFViewer extends ObservableReactComponent { - static _annotationStyle = addStyleSheet(); + static _annotationStyle: any = addStyleSheet(); constructor(props: IViewerProps) { super(props); @@ -65,6 +65,7 @@ export class PDFViewer extends ObservableReactComponent { @observable _pageSizes: { width: number; height: number }[] = []; @observable _savedAnnotations = new ObservableMap(); + @observable _savedTapes = new ObservableMap(); @observable _textSelecting = true; @observable _showWaiting = true; @observable Index: number = -1; @@ -524,8 +525,7 @@ export class PDFViewer extends ObservableReactComponent { if (doc instanceof Doc && property === StyleProp.PointerEvents) { if (this.inlineTextAnnotations.includes(doc) || this._props.isContentActive() === false) return 'none'; const isInk = doc.layout_isSvg && !props?.LayoutTemplateString; - if (isInk) return 'visiblePainted'; - //return isInk ? 'visiblePainted' : 'all'; + return isInk ? 'visiblePainted' : 'all'; } return this._props.styleProvider?.(doc, props, property); }; @@ -582,6 +582,7 @@ export class PDFViewer extends ObservableReactComponent { return
; } savedAnnotations = () => this._savedAnnotations; + savedTapes = () => this._savedTapes; addDocumentWrapper = (doc: Doc | Doc[]) => this._props.addDocument!(doc); render() { TraceMobx(); @@ -615,6 +616,7 @@ export class PDFViewer extends ObservableReactComponent { 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} -- cgit v1.2.3-70-g09d2 From fd5278045e8c2e280d81cb965c0b2cc5afb59be8 Mon Sep 17 00:00:00 2001 From: eleanor-park Date: Wed, 7 Aug 2024 18:09:47 -0400 Subject: problem with setting smooth amount --- package-lock.json | 6 + package.json | 1 + src/client/apis/gpt/GPT.ts | 45 +++++- src/client/cognitive_services/CognitiveServices.ts | 18 ++- src/client/views/GestureOverlay.tsx | 150 ++++++++--------- src/client/views/InkStrokeProperties.ts | 43 ++++- src/client/views/InkTranscription.tsx | 4 +- src/client/views/InkingStroke.tsx | 4 +- src/client/views/PropertiesButtons.tsx | 4 +- src/client/views/PropertiesView.scss | 6 + src/client/views/PropertiesView.tsx | 180 +++++++++++++++++++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 106 ++++++------ .../collectionFreeForm/MarqueeOptionsMenu.tsx | 2 + .../collections/collectionFreeForm/MarqueeView.tsx | 80 ++++++++- .../views/nodes/formattedText/FormattedTextBox.tsx | 8 + src/client/views/pdf/AnchorMenu.tsx | 38 +++++ src/client/views/pdf/PDFViewer.tsx | 10 +- src/client/views/smartdraw/AnnotationPalette.tsx | 44 +---- src/client/views/smartdraw/SmartDrawHandler.tsx | 155 ++++++++++++++---- 19 files changed, 669 insertions(+), 235 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/package-lock.json b/package-lock.json index 70f53156c..7f6237ef5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -214,6 +214,7 @@ "sass-loader": "^14.2.0", "serializr": "^3.0.2", "shelljs": "^0.8.5", + "simplify-js": "^1.2.4", "socket.io": "^4.7.2", "socket.io-client": "^4.7.2", "standard-http-error": "^2.0.1", @@ -38407,6 +38408,11 @@ "node": ">=10" } }, + "node_modules/simplify-js": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/simplify-js/-/simplify-js-1.2.4.tgz", + "integrity": "sha512-vITfSlwt7h/oyrU42R83mtzFpwYk3+mkH9bOHqq/Qw6n8rtR7aE3NZQ5fbcyCUVVmuMJR6ynsAhOfK2qoah8Jg==" + }, "node_modules/skmeans": { "version": "0.9.7", "resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz", diff --git a/package.json b/package.json index 8929bddf0..16b2841be 100644 --- a/package.json +++ b/package.json @@ -299,6 +299,7 @@ "sass-loader": "^14.2.0", "serializr": "^3.0.2", "shelljs": "^0.8.5", + "simplify-js": "^1.2.4", "socket.io": "^4.7.2", "socket.io-client": "^4.7.2", "standard-http-error": "^2.0.1", diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index a780596fa..ee8d5e9b2 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -13,6 +13,7 @@ enum GPTCallType { MERMAID = 'mermaid', DATA = 'data', DRAW = 'draw', + COLOR = 'color', } type GPTCallOpts = { @@ -57,9 +58,15 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { draw: { model: 'gpt-4o', maxTokens: 1024, - temp: 0.5, + temp: 0.8, prompt: 'Given an item, a level of complexity from 1-10, and a size in pixels, generate a detailed and colored line drawing representation of it. Make sure every element has the stroke field filled out. More complex drawings will have much more detail and strokes. The drawing should be in SVG format with no additional text or comments. For path coordinates, make sure you format with a comma between numbers, like M100,200 C150,250 etc. The only supported commands are line, ellipse, circle, rect, polygon, and path with M, Q, C, and L so only use those.', }, + color: { + model: 'gpt-4o', + maxTokens: 1024, + temp: 0.5, + prompt: 'You will be coloring drawings. You will be given what the drawing is, then a list of descriptions for parts of the drawing. Based on each description, respond with the stroke and fill color that it should be. Follow the rules: 1. Avoid using black for stroke color 2. Make the stroke color 1-3 shades darker than the fill color 3. Use the same colors when possible. Format as {#abcdef #abcdef}, making sure theres a color for each description, and do not include any additional text.', + }, }; let lastCall = ''; @@ -187,4 +194,38 @@ const gptHandwriting = async (src: string): Promise => { } }; -export { gptAPICall, gptImageCall, GPTCallType, gptImageLabel, gptGetEmbedding, gptHandwriting }; +const gptDrawingColor = async (image: string, coords: string[]): Promise => { + try { + const response = await openai.chat.completions.create({ + model: 'gpt-4o', + temperature: 0, + messages: [ + { + role: 'user', + content: [ + { + type: 'text', + text: `Identify what the drawing in the image represents in 1-5 words. Then, given a list of a list of coordinates, where each list is the coordinates for one stroke of the drawing, determine which part of the drawing it is. Return just what the item it is, followed by ~~~ then only your descriptions in a list like [description, description, ...]. Here are the coordinates: ${coords}`, + }, + { + type: 'image_url', + image_url: { + url: `${image}`, + detail: 'low', + }, + }, + ], + }, + ], + }); + if (response.choices[0].message.content) { + return response.choices[0].message.content; + } + return 'Missing labels'; + } catch (err) { + console.log(err); + return 'Error connecting with API'; + } +}; + +export { gptAPICall, gptImageCall, GPTCallType, gptImageLabel, gptGetEmbedding, gptHandwriting, gptDrawingColor }; diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index 9f46b8685..9f7701c54 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -47,14 +47,14 @@ export namespace CognitiveServices { const ExecuteQuery = async (service: Service, manager: APIManager, data: D): Promise => { const apiKey = process.env[service.toUpperCase()]; if (apiKey) { - console.log(data) + console.log(data); console.log(`No API key found for ${service}: ensure youe root directory has .env file with _CLIENT_${service.toUpperCase()}.`); return undefined; } let results: any; try { - results = await manager.requester("has", manager.converter(data), service).then(json => JSON.parse(json)); + results = await manager.requester('has', manager.converter(data), service).then(json => JSON.parse(json)); } catch (e) { throw e; } @@ -138,12 +138,14 @@ export namespace CognitiveServices { points: points.map(({ X: x, Y: y }) => `${x},${y}`).join(','), language: 'en-US', })); - console.log(JSON.stringify({ - version: 1, - language: 'en-US', - unit: 'mm', - strokes, - })) + console.log( + JSON.stringify({ + version: 1, + language: 'en-US', + unit: 'mm', + strokes, + }) + ); return JSON.stringify({ version: 1, language: 'en-US', diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 649208989..7eac583dd 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -141,7 +141,7 @@ export class GestureOverlay extends ObservableReactComponent { - const xs = this._points.map(p => p.X); - const ys = this._points.map(p => p.Y); + public static makeBezierPolygon = (points: { X: number; Y: number }[], shape: string, gesture: boolean) => { + const xs = points.map(p => p.X); + const ys = points.map(p => p.Y); let right = Math.max(...xs); let left = Math.min(...xs); let bottom = Math.max(...ys); let top = Math.min(...ys); - const firstx = this._points[0].X; - const firsty = this._points[0].Y; - let lastx = this._points[this._points.length - 2].X; - let lasty = this._points[this._points.length - 2].Y; + const firstx = points[0].X; + const firsty = points[0].Y; + let lastx = points[points.length - 2].X; + let lasty = points[points.length - 2].Y; let fourth = (lastx - firstx) / 4; if (isNaN(fourth) || fourth === 0) { fourth = 0.01; @@ -223,15 +223,15 @@ export class GestureOverlay extends ObservableReactComponent right) { const temp = right; @@ -245,47 +245,47 @@ export class GestureOverlay extends ObservableReactComponent 10) { lasty = firsty; } - this._points.push({ X: firstx, Y: firsty }); - this._points.push({ X: firstx, Y: firsty }); + points.push({ X: firstx, Y: firsty }); + points.push({ X: firstx, Y: firsty }); - this._points.push({ X: lastx, Y: lasty }); - this._points.push({ X: lastx, Y: lasty }); + points.push({ X: lastx, Y: lasty }); + points.push({ X: lastx, Y: lasty }); break; case Gestures.Arrow: { @@ -347,16 +347,16 @@ export class GestureOverlay extends ObservableReactComponent { diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 3920ecc2a..35d628a4e 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -5,8 +5,8 @@ import { Doc, NumListCast, Opt } from '../../fields/Doc'; import { InkData, InkField, InkTool } from '../../fields/InkField'; import { List } from '../../fields/List'; import { listSpec } from '../../fields/Schema'; -import { Cast, NumCast } from '../../fields/Types'; -import { PointData } from '../../pen-gestures/GestureTypes'; +import { Cast, NumCast, toList } from '../../fields/Types'; +import { Gestures, PointData } from '../../pen-gestures/GestureTypes'; import { Point } from '../../pen-gestures/ndollar'; import { DocumentType } from '../documents/DocumentTypes'; import { undoBatch } from '../util/UndoManager'; @@ -14,6 +14,9 @@ import { FitOneCurve } from '../util/bezierFit'; import { InkingStroke } from './InkingStroke'; import { CollectionFreeFormView } from './collections/collectionFreeForm'; import { DocumentView } from './nodes/DocumentView'; +import simplify from 'simplify-js'; +import { GestureUtils } from '../../pen-gestures/GestureUtils'; +import { GestureOverlay } from './GestureOverlay'; export class InkStrokeProperties { // eslint-disable-next-line no-use-before-define @@ -487,4 +490,40 @@ export class InkStrokeProperties { } return inkCopy; }); + + @undoBatch + smoothInkStrokes = (inkDocs: Doc[], tolerance: number = 5) => { + inkDocs.forEach(inkDoc => { + const inkView = DocumentView.getDocumentView(inkDoc); + const inkStroke = inkView?.ComponentView as InkingStroke; + const { inkData } = inkStroke.inkScaledData(); + + const result = inkData.length > 2 && GestureUtils.GestureRecognizer.Recognize([inkData]); + console.log(result); + let polygonPoints: { X: number; Y: number }[] | undefined = undefined; + if (result && (result.Name === 'line' ? result.Score > 0.92 : result.Score > 0.85)) { + switch (result.Name) { + case Gestures.Line: + case Gestures.Triangle: + case Gestures.Rectangle: + case Gestures.Circle: + GestureOverlay.makeBezierPolygon(inkData, result.Name, true); + break; + default: + } + } else { + const polylinePoints = inkData.filter((pt, index) => { return index % 4 === 0 || pt === inkData.lastElement()}).map(pt => { return { x: pt.X, y: pt.Y }; }); // prettier-ignore + if (polylinePoints.length > 2) { + const toKeep = simplify(polylinePoints, tolerance).map(pt => {return { X: pt.x, Y: pt.y }}); // prettier-ignore + for (var i = 4; i < inkData.length - 3; i += 4) { + const contains = toKeep.find(pt => pt.X === inkData[i].X && pt.Y === inkData[i].Y); + if (!contains) { + this._currentPoint = i; + inkView && this.deletePoints(inkView, false); + } + } + } + } + }); + }; } diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx index 3f90df7d1..1277a6fe7 100644 --- a/src/client/views/InkTranscription.tsx +++ b/src/client/views/InkTranscription.tsx @@ -6,7 +6,7 @@ import { InkData, InkField, InkTool } from '../../fields/InkField'; import { Cast, DateCast, ImageCast, NumCast, StrCast } from '../../fields/Types'; import { aggregateBounds } from '../../Utils'; import { DocumentType } from '../documents/DocumentTypes'; -import { CollectionFreeFormView } from './collections/collectionFreeForm'; +import { CollectionFreeFormView, MarqueeView } from './collections/collectionFreeForm'; import { InkingStroke } from './InkingStroke'; import './InkTranscription.scss'; import { Docs } from '../documents/Documents'; @@ -387,7 +387,7 @@ export class InkTranscription extends React.Component { ); docView.props.removeDocument?.(selected); // Gets a collection based on the selected nodes using a marquee view ref - const newCollection = marqViewRef?.getCollection(selected, undefined, true); + const newCollection = MarqueeView.getCollection(selected, undefined, true, { left: 1, top: 1, width: 1, height: 1 }); if (newCollection) { newCollection.width = NumCast(newCollection._width); newCollection.height = NumCast(newCollection._height); diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index ce1c07f2f..48b26d903 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -104,10 +104,10 @@ export class InkingStroke extends ViewBoxAnnotatableComponent() * analyzes the ink stroke and saves the analysis of the stroke to the 'inkAnalysis' field, * and the recognized words to the 'handwriting' */ - analyzeStrokes=()=> { + analyzeStrokes = () => { const data: InkData = this.inkScaledData().inkData ?? []; CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ['inkAnalysis', 'handwriting'], [data]); - } + }; /** * Toggles whether the ink stroke is displayed as an overlay mask or as a regular stroke. diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index edf6df2b9..de1800700 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -15,7 +15,7 @@ import { MdClosedCaption, MdClosedCaptionDisabled, MdGridOff, MdGridOn, MdSubtit import { RxWidth } from 'react-icons/rx'; import { TbEditCircle, TbEditCircleOff, TbHandOff, TbHandStop, TbHighlight, TbHighlightOff } from 'react-icons/tb'; import { TfiBarChart } from 'react-icons/tfi'; -import { Doc, Opt } from '../../fields/Doc'; +import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { ScriptField } from '../../fields/ScriptField'; import { BoolCast, ScriptCast } from '../../fields/Types'; @@ -30,6 +30,8 @@ import { Colors } from './global/globalEnums'; import { DocumentView } from './nodes/DocumentView'; import { OpenWhere } from './nodes/OpenWhere'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; +import { MarqueeOptionsMenu } from './collections/collectionFreeForm'; +import { InkStrokeProperties } from './InkStrokeProperties'; @observer export class PropertiesButtons extends React.Component<{}, {}> { diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss index 840df41e7..aa825a6e9 100644 --- a/src/client/views/PropertiesView.scss +++ b/src/client/views/PropertiesView.scss @@ -638,3 +638,9 @@ padding-left: 8px; background-color: rgb(51, 51, 51); } + +.smooth, +.color, +.smooth-slider { + margin-top: 3px; +} diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 024db82a4..ac2625f32 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -5,7 +5,7 @@ import { IconLookup } from '@fortawesome/fontawesome-svg-core'; import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Checkbox, Tooltip } from '@mui/material'; -import { Colors, EditableText, IconButton, NumberInput, Size, Slider, Type } from 'browndash-components'; +import { Colors, EditableText, IconButton, NumberInput, Size, Slider, Toggle, ToggleType, Type } from 'browndash-components'; import { concat } from 'lodash'; import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; @@ -14,13 +14,13 @@ import { ColorResult, SketchPicker } from 'react-color'; import * as Icons from 'react-icons/bs'; // {BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs" import { ClientUtils, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc'; +import { Doc, DocListCast, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc'; import { AclAdmin, DocAcl, DocData } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; import { List } from '../../fields/List'; import { ComputedField } from '../../fields/ScriptField'; -import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; +import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { GetEffectiveAcl, SharingPermissions, normalizeEmail } from '../../fields/util'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { GroupManager } from '../util/GroupManager'; @@ -43,6 +43,10 @@ import { DocumentView } from './nodes/DocumentView'; import { StyleProviderFuncType } from './nodes/FieldView'; import { OpenWhere } from './nodes/OpenWhere'; import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails'; +import { InkingStroke } from './InkingStroke'; +import { SettingsManager } from '../util/SettingsManager'; +import { MarqueeOptionsMenu } from './collections/collectionFreeForm'; +import { SmartDrawHandler } from './smartdraw/SmartDrawHandler'; const _global = (window /* browser */ || global) /* node */ as any; @@ -117,6 +121,10 @@ export class PropertiesView extends ObservableReactComponent { + doc[DocData].stroke_width = Math.round(value * 100) / 100; + }) + } else { + this.selectedDoc && (this.selectedDoc[DocData].stroke_width = Math.round(value * 100) / 100); + } + } // prettier-ignore @computed get hgtInput() { return this.inputBoxDuo( @@ -843,10 +860,32 @@ export class PropertiesView extends ObservableReactComponent { + const inkStroke = DocumentView.getDocumentView(doc)?.ComponentView as InkingStroke; + const { inkData } = inkStroke.inkScaledData(); + if (InkingStroke.IsClosed(inkData)) { + doc[DocData].fillColor = value || undefined; + } + }); + } else { + this.selectedDoc && (this.selectedDoc[DocData].fillColor = value || undefined); + } + } + @computed get colorStk() { return this.containsInkDoc ? StrCast(this.inkDoc?.[DocData].color) : StrCast(this.selectedDoc?.[DocData].color); } // prettier-ignore + set colorStk(value) { + if (this.containsInkDoc) { + const childDocs = DocListCast(this.selectedDoc[DocData].data); + childDocs.forEach(doc => { + doc[DocData].color = value || undefined; + }); + } else { + this.selectedDoc && (this.selectedDoc[DocData].color = value || undefined); + } + } colorButton(value: string, type: string, setter: () => void) { return ( @@ -917,10 +956,69 @@ export class PropertiesView extends ObservableReactComponent +
+ } + iconPlacement="left" + align="flex-start" + fillWidth + toggleType={ToggleType.BUTTON} + onClick={undoable(() => { + InkStrokeProperties.Instance.smoothInkStrokes(this.containsInkDoc ? DocListCast(targetDoc.data) : [targetDoc], this.smoothAmt); + }, 'smoothStrokes')} + /> +
+
+ {this.getNumber( + 'Smooth Amount', + '', + 1, + Math.max(20, this.smoothAmt), + this.smoothAmt, + (val: number) => { + !isNaN(val) && (this.smoothAmt = val); + }, + 20, + 1 + )} +
+ {!targetDoc.layout_isSvg && ( +
+ } + iconPlacement="left" + align="flex-start" + fillWidth + toggleType={ToggleType.BUTTON} + onClick={undoable(() => { + SmartDrawHandler.Instance.colorWithGPT(targetDoc); + }, 'smoothStrokes')} + /> +
+ )} +
+ ); + } + + @computed get dashdStk() { return this.containsInkDoc? this.inkDoc?.stroke_dash || '' : this.selectedDoc?.stroke_dash || ''; } // prettier-ignore set dashdStk(value) { value && (this._lastDash = value); - this.selectedDoc && (this.selectedDoc[DocData].stroke_dash = value ? this._lastDash : undefined); + if (this.containsInkDoc) { + const childDocs = DocListCast(this.selectedDoc[DocData].data); + childDocs.forEach(doc => { + doc[DocData].stroke_dash = value ? this._lastDash : undefined; + }); + } else { + this.selectedDoc && (this.selectedDoc[DocData].stroke_dash = value ? this._lastDash : undefined); + } } @computed get widthStk() { return this.getField('stroke_width') || '1'; } // prettier-ignore set widthStk(value) { @@ -930,13 +1028,31 @@ export class PropertiesView extends ObservableReactComponent { + doc[DocData].stroke_startMarker = value; + }); + } else { + this.selectedDoc && (this.selectedDoc[DocData].stroke_startMarker = value); + } } @computed get markTail() { return this.getField('stroke_endMarker') || ''; } // prettier-ignore set markTail(value) { - this.selectedDoc && (this.selectedDoc[DocData].stroke_endMarker = value); + if (this.containsInkDoc) { + const childDocs = DocListCast(this.selectedDoc[DocData].data); + childDocs.forEach(doc => { + doc[DocData].stroke_endMarker = value; + }); + } else { + this.selectedDoc && (this.selectedDoc[DocData].stroke_endMarker = value); + } } regInput = (key: string, value: any, setter: (val: string) => {}) => ( @@ -1036,6 +1152,16 @@ export class PropertiesView extends ObservableReactComponent {this.widthAndDash} {this.strokeAndFill} + {this.smoothAndColor} + + ); + } + + @computed get inkEditor() { + return ( +
+ {this.widthAndDash} + {this.strokeAndFill}
); } @@ -1164,6 +1290,7 @@ export class PropertiesView extends ObservableReactComponent @@ -1177,6 +1304,30 @@ export class PropertiesView extends ObservableReactComponent { + const childDocs: Doc[] = DocListCast(selectedDoc[DocData].data); + for (var i = 0; i < childDocs.length; i++) { + if (DocumentView.getDocumentView(childDocs[i])?.layoutDoc?.layout_isSvg) { + this.inkDoc = childDocs[i]; + this.containsInkDoc = true; + return true; + } + } + this.containsInkDoc = false; + return false; + }; + + @computed get inkCollectionSubMenu() { + return ( + // prettier-ignore + <> + { this.openAppearance = bool; }} onDoubleClick={this.CloseAll}> + {this.isGroup && this.containsInk(this.selectedDoc) ? this.appearanceEditor : null} + + + ); + } + @computed get fieldsSubMenu() { return ( { 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, - _height: B.height + inkWidth, - stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore - inkWidth, - ActiveInkColor(), - ActiveInkBezierApprox(), - ActiveFillColor(), - ActiveArrowStart(), - ActiveArrowEnd(), - ActiveDash(), - ActiveIsInkMask() - ); + return this.createInkDoc(points); }); newStrokes && this.addDocument?.(newStrokes); // setTimeout(() => this._eraserLock--); @@ -1276,18 +1240,43 @@ export class CollectionFreeFormView extends CollectionSubView { + const bounds = InkField.getBounds(points); + const B = transformedBounds || 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, + _height: B.height + inkWidth, + stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore + inkWidth, + ActiveInkColor(), + ActiveInkBezierApprox(), + ActiveFillColor(), + ActiveArrowStart(), + ActiveArrowEnd(), + ActiveDash(), + ActiveIsInkMask() + ); + }; + @action - showSmartDraw = (e: PointerEvent, doubleTap?: boolean) => { - SmartDrawHandler.Instance.displaySmartDrawHandler(e.pageX, e.pageY, this.createDrawing, this.removeDrawing); + showSmartDraw = (e: PointerEvent) => { + SmartDrawHandler.Instance.CreateDrawingDoc = this.createDrawingDoc; + SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing; + SmartDrawHandler.Instance.AddDrawing = this.addDrawing; + SmartDrawHandler.Instance.displaySmartDrawHandler(e.pageX, e.pageY); }; _drawing: Doc[] = []; _drawingContainer: Doc | undefined = undefined; @undoBatch - createDrawing = (strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => { + createDrawingDoc = (strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string) => { this._drawing = []; const xf = this.screenToFreeformContentsXf; - // this._drawingContainer = undefined; strokeData.forEach((stroke: [InkData, string, string]) => { const bounds = InkField.getBounds(stroke[0]); const B = xf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height); @@ -1310,20 +1299,9 @@ export class CollectionFreeFormView extends CollectionSubView { @@ -1339,6 +1317,19 @@ export class CollectionFreeFormView extends CollectionSubView { + const docData = doc[DocData]; + docData.title = opts.text.match(/^(.*?)~~~.*$/)?.[1] || opts.text; + docData.drawingInput = opts.text; + docData.drawingComplexity = opts.complexity; + docData.drawingColored = opts.autoColor; + docData.drawingSize = opts.size; + docData.drawingData = gptRes; + this._drawingContainer = doc; + this.addDocument(doc); + this._batch?.end(); + }; + @action zoom = (pointX: number, pointY: number, deltaY: number): void => { if (this.Document.isGroup || this.Document[(this._props.viewField ?? '_') + 'freeform_noZoom']) return; @@ -2045,7 +2036,10 @@ export class CollectionFreeFormView extends CollectionSubView { - !SmartDrawHandler.Instance._showRegenerate ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10, this.createDrawing, this.removeDrawing) : SmartDrawHandler.Instance.hideRegenerate(); + SmartDrawHandler.Instance.CreateDrawingDoc = this.createDrawingDoc; + SmartDrawHandler.Instance.AddDrawing = this.addDrawing; + SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing; + !SmartDrawHandler.Instance._showRegenerate ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10) : SmartDrawHandler.Instance.hideRegenerate(); }), icon: 'pen-to-square', }); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx index b3fdd9379..76c37dff0 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx @@ -21,6 +21,7 @@ export class MarqueeOptionsMenu extends AntimodeMenu { public pinWithView: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction; public classifyImages: (e: React.MouseEvent | undefined) => void = unimplementedFunction; public groupImages: () => void = unimplementedFunction; + public smoothStrokes: (docs?: Doc[]) => void = unimplementedFunction; public isShown = () => this._opacity > 0; constructor(props: any) { super(props); @@ -41,6 +42,7 @@ export class MarqueeOptionsMenu extends AntimodeMenu { } color={this.userColor} /> } color={this.userColor} /> } color={this.userColor} /> + this.smoothStrokes} icon={} color={this.userColor} /> ); return this.getElement(buttons); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 8560323c9..92c0da983 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -4,14 +4,14 @@ import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { ClientUtils, lightOrDark, returnFalse } from '../../../../ClientUtils'; -import { intersectRect, numberRange } from '../../../../Utils'; +import { intersectRect, numberRange, unimplementedFunction } from '../../../../Utils'; import { Doc, NumListCast, Opt } from '../../../../fields/Doc'; import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { InkData, InkField, InkTool } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; import { RichTextField } from '../../../../fields/RichTextField'; -import { Cast, FieldValue, ImageCast, NumCast, StrCast } from '../../../../fields/Types'; +import { BoolCast, Cast, DocCast, FieldValue, ImageCast, NumCast, StrCast } from '../../../../fields/Types'; import { ImageField } from '../../../../fields/URLField'; import { GetEffectiveAcl } from '../../../../fields/util'; import { gptGetEmbedding, gptImageLabel } from '../../../apis/gpt/GPT'; @@ -26,7 +26,7 @@ import { ContextMenu } from '../../ContextMenu'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { MarqueeViewBounds } from '../../PinFuncs'; import { PreviewCursor } from '../../PreviewCursor'; -import { DocumentView } from '../../nodes/DocumentView'; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, DocumentView } from '../../nodes/DocumentView'; import { OpenWhere } from '../../nodes/OpenWhere'; import { pasteImageBitmap } from '../../nodes/WebBoxRenderer'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; @@ -36,7 +36,12 @@ import { CollectionFreeFormView } from './CollectionFreeFormView'; import { ImageLabelHandler } from './ImageLabelHandler'; import { MarqueeOptionsMenu } from './MarqueeOptionsMenu'; import './MarqueeView.scss'; -import { collectionOf } from '@turf/turf'; +import { collectionOf, points } from '@turf/turf'; +import { InkingStroke } from '../../InkingStroke'; +import { GestureUtils } from '../../../../pen-gestures/GestureUtils'; +import { Gestures } from '../../../../pen-gestures/GestureTypes'; +import { GestureOverlay } from '../../GestureOverlay'; +import { InkStrokeProperties } from '../../InkStrokeProperties'; interface MarqueeViewProps { getContainerTransform: () => Transform; @@ -89,6 +94,8 @@ export class MarqueeView extends ObservableReactComponent Doc | void = unimplementedFunction; + componentDidMount() { this._props.setPreviewCursor?.(this.setPreviewCursor); } @@ -277,6 +284,7 @@ export class MarqueeView extends ObservableReactComponent { + docs && docs.length > 0 ? (this._selectedDocs = docs) : (this._selectedDocs = this.marqueeSelect(false, DocumentType.INK)); + if (this._selectedDocs.length == 0) return; + + this._selectedDocs.forEach(stroke => { + const docView = DocumentView.getDocumentView(stroke); + const inkStroke = docView?.ComponentView as InkingStroke; + const { inkData } = inkStroke.inkScaledData(); + + const result = inkData.length > 2 && GestureUtils.GestureRecognizer.Recognize([inkData]); + console.log(result); + let polygonPoints: { X: number; Y: number }[] | undefined = undefined; + if (result && (result.Name === 'line' ? result.Score > 0.9 : result.Score > 0.8)) { + switch (result.Name) { + case Gestures.Line: + case Gestures.Triangle: + case Gestures.Rectangle: + case Gestures.Circle: + GestureOverlay.makeBezierPolygon(inkData, result.Name, true); + break; + default: + } + } else { + const distances: number[] = []; + for (var i = 0; i < inkData.length - 3; i += 4) { + distances.push(Math.sqrt((inkData[i].X - inkData[i + 3].X) ** 2 + (inkData[i].Y - inkData[i + 3].Y) ** 2)); + } + const avgDist = (NumCast(stroke.width) + NumCast(stroke.height)) / 2; + // const avgDist = distances.reduce((a, b) => a + b) / distances.length; + if (Math.sqrt((inkData.lastElement().X - inkData[0].X) ** 2 + (inkData.lastElement().Y - inkData[0].Y) ** 2) < avgDist) { + inkData.pop(); + inkData.push({ X: inkData[0].X, Y: inkData[0].Y }); + } + // const editedPoints: InkData = []; + // const toDelete: number[] = []; + + // distances.forEach((dist, i) => { + // if (dist < avgDist / 3) { + // toDelete.unshift(i * 4); + // } + // }); + // toDelete.forEach(pt => { + // InkStrokeProperties.Instance._currentPoint = pt; + // docView && InkStrokeProperties.Instance.deletePoints(docView, false); + // }); + + // for (var i = 0; i < distances.length; i++) { + // if (distances[i] > avgDist / 3) { + // editedPoints.push(...inkData.slice(i * 4, i * 4 + 4)); + // } else { + // if (i !== distances.length) { + // editedPoints.push(...inkData.slice(i * 4, i * 4 + 2)); + // editedPoints.push(...inkData.slice(i * 4 + 6, i * 4 + 8)); + // i++; + // } + // } + // } + // inkData.length = 0; + // inkData.push(...editedPoints); + } + }); + }); + @undoBatch syntaxHighlight = action((e: KeyboardEvent | React.PointerEvent | undefined) => { const selected = this.marqueeSelect(false); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 9f2a9b8e1..1e700d240 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -295,6 +295,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this.getAnchor(true), targetCreator), e.pageX, e.pageY); }); + AnchorMenu.Instance.AddDrawingAnnotation = (drawing: Doc) => { + const container = DocCast(this._props.Document.embedContainer); + const docView = DocumentView.getDocumentView?.(container); + docView?.ComponentView?._props.addDocument?.(drawing); + drawing.x = NumCast(this._props.Document.x) + (this._props.Document.width as number); + drawing.y = NumCast(this._props.Document.y); + }; + AnchorMenu.Instance.setSelectedText(window.getSelection()?.toString() ?? ''); const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to); this._props.rootSelected?.() && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom); diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index df990b0c0..ea574493a 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -15,6 +15,11 @@ import { LinkPopup } from '../linking/LinkPopup'; import { DocumentView } from '../nodes/DocumentView'; import './AnchorMenu.scss'; import { GPTPopup, GPTPopupMode } from './GPTPopup/GPTPopup'; +import { DrawingOptions, SmartDrawHandler } from '../smartdraw/SmartDrawHandler'; +import { InkData, InkField } from '../../../fields/InkField'; +import { DocData } from '../../../fields/DocSymbols'; +import { undoBatch } from '../../util/UndoManager'; +import ReactLoading from 'react-loading'; @observer export class AnchorMenu extends AntimodeMenu { @@ -38,6 +43,7 @@ export class AnchorMenu extends AntimodeMenu { // GPT additions @observable private _selectedText: string = ''; + @observable private _isLoading: boolean = false; @action public setSelectedText = (txt: string) => { this._selectedText = txt.trim(); @@ -61,6 +67,7 @@ export class AnchorMenu extends AntimodeMenu { public get Active() { return this._left > 0; } + public AddDrawingAnnotation: (doc: Doc) => void = unimplementedFunction; public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined; componentWillUnmount() { @@ -137,6 +144,29 @@ export class AnchorMenu extends AntimodeMenu { this.addToCollection?.(newCol); }; + gptDraw = async (e: React.PointerEvent) => { + try { + SmartDrawHandler.Instance.AddDrawing = this.createDrawingAnnotation; + this._isLoading = true; + await SmartDrawHandler.Instance.drawWithGPT({ X: e.clientX, Y: e.clientY }, this._selectedText, 5, 100, true); + this._isLoading = false; + } catch (err) { + console.error(err); + } + }; + + @undoBatch + createDrawingAnnotation = action((drawing: Doc, opts: DrawingOptions, gptRes: string) => { + this.AddDrawingAnnotation(drawing); + const docData = drawing[DocData]; + docData.title = opts.text.match(/^(.*?)~~~.*$/)?.[1] || opts.text; + docData.drawingInput = opts.text; + docData.drawingComplexity = opts.complexity; + docData.drawingColored = opts.autoColor; + docData.drawingSize = opts.size; + docData.drawingData = gptRes; + }); + pointerDown = (e: React.PointerEvent) => { setupMoveUpEvents( this, @@ -239,6 +269,14 @@ export class AnchorMenu extends AntimodeMenu { icon={} color={SettingsManager.userColor} /> + {this._selectedText && ( + this.gptDraw(e)} + icon={this._isLoading ? : } + color={SettingsManager.userColor} + /> + )} {AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : ( { GPTPopup.Instance.addDoc = this._props.sidebarAddDoc; // allows for creating collection AnchorMenu.Instance.addToCollection = this._props.DocumentView?.()._props.addDocument; + AnchorMenu.Instance.AddDrawingAnnotation = this.addDrawingAnnotation; + }; + + addDrawingAnnotation = (drawing: Doc) => { + // drawing[DocData].x = this._props.pdfBox.ScreenToLocalBoxXf().TranslateX + // const scaleX = this._mainCont.current.offsetWidth / boundingRect.width; + drawing.y = (drawing.y as number) + (this._props.Document.data_sidebar_panY as number); + this._props.addDocument?.(drawing); }; @action diff --git a/src/client/views/smartdraw/AnnotationPalette.tsx b/src/client/views/smartdraw/AnnotationPalette.tsx index ec4279e3e..7e4d46204 100644 --- a/src/client/views/smartdraw/AnnotationPalette.tsx +++ b/src/client/views/smartdraw/AnnotationPalette.tsx @@ -64,7 +64,7 @@ export class AnnotationPalette extends ObservableReactComponent { if (event.key === 'Enter') { - await this.generateDrawing(); + await this.generateDrawings(); } }; @@ -130,15 +130,14 @@ export class AnnotationPalette extends ObservableReactComponent { + generateDrawings = action(async () => { this._isLoading = true; this._props.Document[DocData].data = undefined; for (var i = 0; i < 3; i++) { try { - SmartDrawHandler.Instance._addFunc = this.createDrawing; + SmartDrawHandler.Instance.AddDrawing = this.addDrawing; this._canInteract = false; if (this._showRegenerate) { - SmartDrawHandler.Instance._deleteFunc = unimplementedFunction; await SmartDrawHandler.Instance.regenerate(this._opts, this._gptRes[i], this._userInput); } else { await SmartDrawHandler.Instance.drawWithGPT({ X: 0, Y: 0 }, this._userInput, this._opts.complexity, this._opts.size, this._opts.autoColor); @@ -154,39 +153,10 @@ export class AnnotationPalette extends ObservableReactComponent { - this._opts = opts; + addDrawing = (drawing: Doc, opts: DrawingOptions, gptRes: string) => { this._gptRes.push(gptRes); - const drawing: Doc[] = []; - - strokeList.forEach((stroke: [InkData, string, string]) => { - const bounds = InkField.getBounds(stroke[0]); - const inkWidth = Math.min(5, ActiveInkWidth()); - const inkDoc = Docs.Create.InkDocument( - stroke[0], - { title: 'stroke', - x: bounds.left - inkWidth / 2, - y: bounds.top - inkWidth / 2, - _width: bounds.width + inkWidth, - _height: bounds.height + inkWidth, - stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore - inkWidth, - opts.autoColor ? stroke[1] : ActiveInkColor(), - ActiveInkBezierApprox(), - stroke[2] === 'none' ? ActiveFillColor() : stroke[2], - ActiveArrowStart(), - ActiveArrowEnd(), - ActiveDash(), - ActiveIsInkMask() - ); - drawing.push(inkDoc); - }); - - const collection = MarqueeView.getCollection(drawing, undefined, true, { left: 1, top: 1, width: 1, height: 1 }); - if (collection) { - collection[DocData].freeform_fitContentsToBox = true; - Doc.AddDocToList(this._props.Document, 'data', collection); - } + drawing[DocData].freeform_fitContentsToBox = true; + Doc.AddDocToList(this._props.Document, 'data', drawing); }; saveDrawing = async () => { @@ -262,7 +232,7 @@ export class AnnotationPalette extends ObservableReactComponent : this._showRegenerate ? : } iconPlacement="right" color={SettingsManager.userColor} - onClick={this.generateDrawing} + onClick={this.generateDrawings} />
diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx index c842551c3..52df598ee 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.tsx +++ b/src/client/views/smartdraw/SmartDrawHandler.tsx @@ -7,17 +7,23 @@ import { ObservableReactComponent } from '../ObservableReactComponent'; import { Button, IconButton } from 'browndash-components'; import ReactLoading from 'react-loading'; import { AiOutlineSend } from 'react-icons/ai'; -import { gptAPICall, GPTCallType } from '../../apis/gpt/GPT'; -import { InkData, InkTool } from '../../../fields/InkField'; +import { gptAPICall, GPTCallType, gptDrawingColor } from '../../apis/gpt/GPT'; +import { InkData, InkField, InkTool } from '../../../fields/InkField'; import { SVGToBezier } from '../../util/bezierFit'; const { parse } = require('svgson'); import { Slider, Switch } from '@mui/material'; -import { Doc } from '../../../fields/Doc'; +import { Doc, DocListCast } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; -import { DocumentView } from '../nodes/DocumentView'; -import { BoolCast, NumCast, StrCast } from '../../../fields/Types'; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, DocumentView } from '../nodes/DocumentView'; +import { BoolCast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; import './SmartDrawHandler.scss'; import { unimplementedFunction } from '../../../Utils'; +import { Docs } from '../../documents/Documents'; +import { MarqueeView } from '../collections/collectionFreeForm'; +import { ImageField, URLField } from '../../../fields/URLField'; +import { CollectionCardView } from '../collections/CollectionCardDeckView'; +import { InkingStroke } from '../InkingStroke'; +import { undoBatch } from '../../util/UndoManager'; export interface DrawingOptions { text: string; @@ -46,13 +52,43 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { @observable private _autoColor: boolean = true; @observable private _regenInput: string = ''; @observable private _canInteract: boolean = true; - public _addFunc: (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void = () => {}; - public _deleteFunc: (doc?: Doc) => void = () => {}; + private _lastInput: DrawingOptions = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 }; private _lastResponse: string = ''; private _selectedDoc: Doc | undefined = undefined; private _errorOccurredOnce = false; + public RemoveDrawing: (doc?: Doc) => void = unimplementedFunction; + public CreateDrawingDoc: (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => Doc | undefined = (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string) => { + const drawing: Doc[] = []; + strokeList.forEach((stroke: [InkData, string, string]) => { + const bounds = InkField.getBounds(stroke[0]); + const inkWidth = Math.min(5, ActiveInkWidth()); + const inkDoc = Docs.Create.InkDocument( + stroke[0], + { title: 'stroke', + x: bounds.left - inkWidth / 2, + y: bounds.top - inkWidth / 2, + _width: bounds.width + inkWidth, + _height: bounds.height + inkWidth, + stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore + inkWidth, + opts.autoColor ? stroke[1] : ActiveInkColor(), + ActiveInkBezierApprox(), + stroke[2] === 'none' ? ActiveFillColor() : stroke[2], + ActiveArrowStart(), + ActiveArrowEnd(), + ActiveDash(), + ActiveIsInkMask() + ); + drawing.push(inkDoc); + }); + + const collection = MarqueeView.getCollection(drawing, undefined, true, { left: 1, top: 1, width: 1, height: 1 }); + return collection; + }; + public AddDrawing: (doc: Doc, opts: DrawingOptions, gptRes: string) => void = unimplementedFunction; + constructor(props: any) { super(props); makeObservable(this); @@ -90,25 +126,26 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { }; @action - displaySmartDrawHandler = (x: number, y: number, addFunc: (strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void, deleteFunc: (doc?: Doc) => void) => { + setEdit = () => { + this._showEditBox = !this._showEditBox; + }; + + @action + displaySmartDrawHandler = (x: number, y: number) => { this._pageX = x; this._pageY = y; this._display = true; - this._addFunc = addFunc; - this._deleteFunc = deleteFunc; }; @action - displayRegenerate = (x: number, y: number, addFunc: (strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void, deleteFunc: (doc?: Doc) => void) => { + displayRegenerate = (x: number, y: number) => { this._selectedDoc = DocumentView.SelectedDocs()?.lastElement(); - const docData = this._selectedDoc[DocData]; - this._addFunc = addFunc; - this._deleteFunc = deleteFunc; this._pageX = x; this._pageY = y; this._display = false; this._showRegenerate = true; this._showEditBox = false; + const docData = this._selectedDoc[DocData]; this._lastResponse = StrCast(docData.drawingData); this._lastInput = { text: StrCast(docData.drawingInput), complexity: NumCast(docData.drawingComplexity), size: NumCast(docData.drawingSize), autoColor: BoolCast(docData.drawingColored), x: this._pageX, y: this._pageY }; }; @@ -155,8 +192,8 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { this._showOptions = false; try { await this.drawWithGPT({ X: this._pageX, Y: this._pageY }, this._userInput, this._complexity, this._size, this._autoColor); - this._showRegenerate = true; this.hideSmartDrawHandler(); + this._showRegenerate = true; } catch (err) { if (this._errorOccurredOnce) { console.error('GPT call failed', err); @@ -181,16 +218,14 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { return; } console.log(res); - const strokeData = await this.parseResponse(res, startPt, false, autoColor); + const strokeData = await this.parseSvg(res, startPt, false, autoColor); + const drawingDoc = strokeData && this.CreateDrawingDoc(strokeData?.data, strokeData?.lastInput, strokeData?.lastRes); + drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res); + this._errorOccurredOnce = false; return strokeData; }; - @action - edit = () => { - this._showEditBox = !this._showEditBox; - }; - @action regenerate = async (lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string) => { if (lastInput) this._lastInput = lastInput; @@ -211,14 +246,18 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { return; } console.log(res); - await this.parseResponse(res, { X: this._lastInput.x, Y: this._lastInput.y }, true, lastInput?.autoColor || this._autoColor); + const strokeData = await this.parseSvg(res, { X: this._lastInput.x, Y: this._lastInput.y }, true, lastInput?.autoColor || this._autoColor); + this.RemoveDrawing !== unimplementedFunction && this.RemoveDrawing(this._selectedDoc); + const drawingDoc = strokeData && this.CreateDrawingDoc(strokeData?.data, strokeData?.lastInput, strokeData?.lastRes); + drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res); + return strokeData; } catch (err) { - console.error('GPT call failed', err); + console.error('Error regenerating drawing', err); } }; @action - parseResponse = async (res: string, startPoint: { X: number; Y: number }, regenerate: boolean, autoColor: boolean) => { + parseSvg = async (res: string, startPoint: { X: number; Y: number }, regenerate: boolean, autoColor: boolean) => { const svg = res.match(/]*>([\s\S]*?)<\/svg>/g); if (svg) { this._lastResponse = svg[0]; @@ -235,16 +274,70 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { (regenerate ? this._lastInput.autoColor : autoColor) ? child.attributes.fill : undefined, ]); }); - if (regenerate) { - if (this._deleteFunc !== unimplementedFunction) this._deleteFunc(this._selectedDoc); - this._addFunc(strokeData, this._lastInput, svg[0]); - } else { - this._addFunc(strokeData, this._lastInput, svg[0]); - } return { data: strokeData, lastInput: this._lastInput, lastRes: svg[0] }; } }; + colorWithGPT = async (drawing: Doc) => { + const img = await this.getIcon(drawing); + const { href } = (img as URLField).url; + const hrefParts = href.split('.'); + const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`; + try { + const hrefBase64 = await CollectionCardView.imageUrlToBase64(hrefComplete); + const strokes = DocListCast(drawing[DocData].data); + const coords: string[] = []; + strokes.forEach((stroke, i) => { + const inkingStroke = DocumentView.getDocumentView(stroke)?.ComponentView as InkingStroke; + const { inkData } = inkingStroke.inkScaledData(); + coords.push( + `${i + 1}. ${inkData + .filter((point, index) => { + return index % 4 === 0 || index == inkData.length - 1; + }) + .map(point => { + return `(${point.X.toString()}, ${point.Y.toString()})`; + })}` + ); + }); + const response = await gptDrawingColor(hrefBase64, coords); + console.log(response); + const colorResponse = await gptAPICall(response, GPTCallType.COLOR, undefined); + console.log(colorResponse); + this.colorStrokes(colorResponse, drawing); + } catch (error) { + console.log('GPT call failed'); + } + }; + + @undoBatch + colorStrokes = (res: string, drawing: Doc) => { + const colorList = res.match(/\{.*?\}/g); + const strokes = DocListCast(drawing[DocData].data); + colorList?.forEach((colors, index) => { + const strokeAndFill = colors.match(/#[0-9A-Fa-f]{6}/g); + if (strokeAndFill && strokeAndFill.length == 2) { + strokes[index][DocData].color = strokeAndFill[0]; + const inkStroke = DocumentView.getDocumentView(strokes[index])?.ComponentView as InkingStroke; + const { inkData } = inkStroke.inkScaledData(); + if (InkingStroke.IsClosed(inkData)) { + strokes[index][DocData].fillColor = strokeAndFill[1]; + } + } + }); + }; + + async getIcon(doc: Doc) { + const docView = DocumentView.getDocumentView(doc); + console.log(doc); + if (docView) { + console.log(docView); + docView.ComponentView?.updateIcon?.(); + return new Promise(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000)); + } + return undefined; + } + render() { if (this._display) { return ( @@ -397,7 +490,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { color={SettingsManager.userColor} onClick={this.handleSendClick} /> - } color={SettingsManager.userColor} onClick={this.edit} /> + } color={SettingsManager.userColor} onClick={this.setEdit} /> {this._showEditBox && (
Date: Tue, 27 Aug 2024 16:58:22 -0400 Subject: resolved merge conflicts --- src/client/views/nodes/DiagramBox.tsx | 136 +--------------------------------- src/client/views/pdf/PDFViewer.tsx | 3 - 2 files changed, 3 insertions(+), 136 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/views/nodes/DiagramBox.tsx b/src/client/views/nodes/DiagramBox.tsx index 602bd4990..36deb2d8d 100644 --- a/src/client/views/nodes/DiagramBox.tsx +++ b/src/client/views/nodes/DiagramBox.tsx @@ -1,5 +1,3 @@ -/* eslint-disable prettier/prettier */ -/* eslint-disable jsx-a11y/control-has-associated-label */ import mermaid from 'mermaid'; import { action, computed, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; @@ -19,8 +17,7 @@ import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { InkingStroke } from '../InkingStroke'; import './DiagramBox.scss'; import { FieldView, FieldViewProps } from './FieldView'; -import { PointData } from '../../../pen-gestures/GestureTypes'; -import { InkField } from '../../../fields/InkField'; +import { FormattedTextBox } from './formattedText/FormattedTextBox'; @observer export class DiagramBox extends ViewBoxAnnotatableComponent() { @@ -53,9 +50,7 @@ export class DiagramBox extends ViewBoxAnnotatableComponent() { mermaid.initialize({ securityLevel: 'loose', startOnLoad: true, - darkMode: true, - flowchart: { useMaxWidth: false, htmlLabels: true, curve: 'cardinal' }, - gantt: { useMaxWidth: true, useWidth: 2000 }, + flowchart: { useMaxWidth: true, htmlLabels: true, curve: 'cardinal' }, }); // when a new doc/text/ink/shape is created in the freeform view, this generates the corresponding mermaid diagram code reaction( @@ -178,7 +173,7 @@ export class DiagramBox extends ViewBoxAnnotatableComponent() { return '( )'; }; - renderGpt(): React.ReactNode { + render() { return (
@@ -202,131 +197,6 @@ export class DiagramBox extends ViewBoxAnnotatableComponent() {
); } - exampleButton = () => { - if (this.isExampleMenuOpen) { - this.isExampleMenuOpen = false; - } else { - this.isExampleMenuOpen = true; - } - }; - flowButton = () => { - this.createInputValue = `flowchart TD - A[Christmas] -->|Get money| B(Go shopping) - B --> C{Let me think} - C -->|One| D[Laptop] - C -->|Two| E[iPhone] - C -->|Three| F[fa:fa-car Car]`; - this.renderMermaidAsync(this.createInputValue); - }; - pieButton = () => { - this.createInputValue = `pie title Pets adopted by volunteers - "Dogs" : 386 - "Cats" : 85 - "Rats" : 15`; - this.renderMermaidAsync(this.createInputValue); - }; - timelineButton = () => { - this.createInputValue = `gantt - title A Gantt Diagram - dateFormat YYYY-MM-DD - section Section - A task :a1, 2014-01-01, 30d - Another task :after a1 , 20d - section Another - Task in sec :2014-01-12 , 12d - another task : 24d`; - this.renderMermaidAsync(this.createInputValue); - }; - classButton = () => { - this.createInputValue = `classDiagram - Animal <|-- Duck - Animal <|-- Fish - Animal <|-- Zebra - Animal : +int age - Animal : +String gender - Animal: +isMammal() - Animal: +mate() - class Duck{ - +String beakColor - +swim() - +quack() - } - class Fish{ - -int sizeInFeet - -canEat() - } - class Zebra{ - +bool is_wild - +run() - }`; - this.renderMermaidAsync(this.createInputValue); - }; - mindmapButton = () => { - this.createInputValue = `mindmap - root((mindmap)) - Origins - Long history - ::icon(fa fa-book) - Popularisation - British popular psychology author Tony Buzan - Research - On effectivness
and features - On Automatic creation - Uses - Creative techniques - Strategic planning - Argument mapping - Tools - Pen and paper - Mermaid`; - this.renderMermaidAsync(this.createInputValue); - }; - handleInputChangeEditor = (e: React.ChangeEvent) => { - if (typeof e.target.value === 'string') { - this.createInputValue = e.target.value; - this.renderMermaidAsync(e.target.value); - } - }; - removeWhitespace(str: string): string { - return str.replace(/\s+/g, ''); - } - autoResize(element: HTMLTextAreaElement): void { - element.style.height = '5px'; - element.style.height = element.scrollHeight + 'px'; - } - timeline = `gantt - title College Timeline - dateFormat YYYY-MM-DD - section Semester 1 - Orientation :done, des1, 2023-08-01, 2023-08-03 - Classes Start :active, des2, 2023-08-04, 2023-12-15 - Midterm Exams : des3, 2023-10-15, 2023-10-20 - End of Semester : des4, 2023-12-16, 2023-12-20 - section Semester 2 - Classes Start : des5, 2024-01-10, 2024-05-15 - Spring Break : des6, 2024-03-15, 2024-03-22 - Midterm Exams : des7, 2024-03-25, 2024-03-30 - Final Exams : des8, 2024-05-10, 2024-05-15 - section Summer Break - Internship : des9, 2024-06-01, 2024-08-31 - section Semester 3 - Classes Start : des10, 2024-09-01, 2025-12-15 - Midterm Exams : des11, 2024-11-15, 2024-11-20 - End of Semester : des12, 2025-12-16, 2025-12-20 - section Semester 4 - Classes Start : des13, 2025-01-10, 2025-05-15 - Spring Break : des14, 2025-03-15, 2025-03-22 - Midterm Exams : des15, 2025-03-25, 2025-03-30 - Final Exams : des16, 2025-05-10, 2025-05-15 - Graduation : des17, 2025-05-20, 2025-05-21`; - render() { - this.switchRenderDiv(); - return ( -
- {this.renderDiv} -
- ); - } } Docs.Prototypes.TemplateMap.set(DocumentType.DIAGRAM, { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 3a2b8884e..5984905d0 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -61,7 +61,6 @@ export class PDFViewer extends ObservableReactComponent { @observable _pageSizes: { width: number; height: number }[] = []; @observable _savedAnnotations = new ObservableMap(); - @observable _savedTapes = new ObservableMap(); @observable _textSelecting = true; @observable _showWaiting = true; @observable Index: number = -1; @@ -580,7 +579,6 @@ export class PDFViewer extends ObservableReactComponent { return
; } savedAnnotations = () => this._savedAnnotations; - savedTapes = () => this._savedTapes; addDocumentWrapper = (doc: Doc | Doc[]) => this._props.addDocument!(doc); render() { TraceMobx(); @@ -614,7 +612,6 @@ export class PDFViewer extends ObservableReactComponent { 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} -- cgit v1.2.3-70-g09d2 From 0ac79ba6a7ab19b4aafbc11dac9bab4781d4bd40 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 29 Aug 2024 13:07:53 -0400 Subject: merge cleanup cleanup --- eslint.config.mjs | 67 +++++- src/ClientUtils.ts | 9 +- src/client/cognitive_services/CognitiveServices.ts | 15 +- src/client/util/Scripting.ts | 4 +- src/client/util/SettingsManager.tsx | 6 +- src/client/util/bezierFit.ts | 18 +- src/client/views/DocumentButtonBar.tsx | 10 +- src/client/views/GestureOverlay.tsx | 10 +- src/client/views/InkStrokeProperties.ts | 268 +++++++++++---------- src/client/views/PropertiesButtons.tsx | 4 +- .../collections/CollectionStackedTimeline.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 4 - src/client/views/collections/TreeView.tsx | 6 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 22 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 12 +- src/client/views/pdf/Annotation.tsx | 1 + src/client/views/pdf/PDFViewer.tsx | 4 +- src/client/views/smartdraw/AnnotationPalette.tsx | 20 +- src/client/views/smartdraw/SmartDrawHandler.tsx | 2 +- 20 files changed, 256 insertions(+), 230 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/eslint.config.mjs b/eslint.config.mjs index 12ad3300a..619966f20 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,14 +1,57 @@ -import globals from "globals"; -import pluginJs from "@eslint/js"; -import tseslint from "typescript-eslint"; -import pluginReact from "eslint-plugin-react"; - +import pluginJs from '@eslint/js'; +import pluginReactConfig from 'eslint-plugin-react/configs/recommended.js'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; export default [ - {files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"]}, - {files: ["**/*.js"], languageOptions: {sourceType: "commonjs"}}, - {languageOptions: { globals: globals.browser }}, - pluginJs.configs.recommended, - ...tseslint.configs.recommended, - pluginReact.configs.flat.recommended, -]; \ No newline at end of file + { + languageOptions: { globals: { ...globals.browser, ...globals.node } }, + }, + pluginJs.configs.recommended, + ...tseslint.configs.recommended, + { + rules: { + 'node/no-missing-import': 0, + 'no-console': 'off', + 'func-names': 'off', + 'no-process-exit': 'off', + 'object-shorthand': 'off', + 'class-methods-use-this': 'off', + 'single-quote': 'off', + 'max-classes-per-file': 0, + + 'react/jsx-filename-extension': [ + 2, + { + extensions: ['.js', '.jsx', '.ts', '.tsx'], + }, + ], + + 'import/prefer-default-export': 'off', + 'no-unused-expressions': 'off', + '@typescript-eslint/no-unused-expressions': 'off', + 'prefer-template': 'off', + 'no-inner-declarations': 'off', + 'no-plusplus': 'off', + 'no-multi-assign': 'off', + 'no-underscore-dangle': 'off', + 'no-nested-ternary': 'off', + 'lines-between-class-members': 'off', + 'no-shadow': 'off', + '@typescript-eslint/no-shadow': 'warn', + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': 'error', + '@typescript-eslint/no-namespace': 'off', + 'react/destructuring-assignment': 0, + 'prefer-arrow-callback': 'error', + 'no-return-assign': 'error', + 'no-await-in-loop': 'error', + 'no-loop-func': 'error', + 'no-cond-assign': 'error', + 'no-use-before-define': 'error', + 'no-explicit-any': 'error', + 'no-restricted-globals': ['error', 'event'], + }, + }, + pluginReactConfig, +]; diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts index dc52218c5..55801df81 100644 --- a/src/ClientUtils.ts +++ b/src/ClientUtils.ts @@ -82,10 +82,6 @@ export function returnEmptyFilter() { return [] as string[]; } -export function returnEmptyDoclist() { - return [] as any[]; -} - export namespace ClientUtils { export const CLICK_TIME = 300; export const DRAG_THRESHOLD = 4; @@ -449,11 +445,10 @@ export function smoothScrollHorizontal(duration: number, element: HTMLElement | animateScroll(); } -export function addStyleSheet(styleType: string = 'text/css') { +export function addStyleSheet() { const style = document.createElement('style'); - style.type = styleType; const sheets = document.head.appendChild(style); - return (sheets as any).sheet; + return sheets.sheet; } export function addStyleSheetRule(sheet: CSSStyleSheet | null, selector: string, css: string | { [key: string]: string }, selectorPrefix = '.') { const propText = diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index 9f7701c54..3ee61cbfb 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -46,15 +46,14 @@ export enum Confidence { export namespace CognitiveServices { const ExecuteQuery = async (service: Service, manager: APIManager, data: D): Promise => { const apiKey = process.env[service.toUpperCase()]; - if (apiKey) { - console.log(data); + if (!apiKey) { console.log(`No API key found for ${service}: ensure youe root directory has .env file with _CLIENT_${service.toUpperCase()}.`); return undefined; } let results: any; try { - results = await manager.requester('has', manager.converter(data), service).then(json => JSON.parse(json)); + results = await manager.requester(apiKey, manager.converter(data), service).then(json => JSON.parse(json)); } catch (e) { throw e; } @@ -138,14 +137,6 @@ export namespace CognitiveServices { points: points.map(({ X: x, Y: y }) => `${x},${y}`).join(','), language: 'en-US', })); - console.log( - JSON.stringify({ - version: 1, - language: 'en-US', - unit: 'mm', - strokes, - }) - ); return JSON.stringify({ version: 1, language: 'en-US', @@ -345,7 +336,7 @@ export namespace CognitiveServices { 'Ocp-Apim-Subscription-Key': apiKey, }, }; - return request.post(options); + return rp.post(options); }, }; diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index cb314e3f1..c63d3d7cb 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -1,7 +1,7 @@ // export const ts = (window as any).ts; // import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts' // import * as typescriptes5 from '!!raw-loader!../../../node_modules/typescript/lib/lib.es5.d.ts' -// import typescriptlib from 'type_decls.d'; +import typescriptlib from 'type_decls.d'; import * as ts from 'typescript'; import { Doc, FieldType } from '../../fields/Doc'; import { RefField } from '../../fields/RefField'; @@ -248,7 +248,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp const funcScript = `(function(${paramString})${reqTypes} { ${body} })`; host.writeFile('file.ts', funcScript); - // if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib); + if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib); const program = ts.createProgram(['file.ts'], {}, host); const testResult = program.emit(); const outputText = host.readFile('file.js'); diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 9e49117a7..9200d68db 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -27,7 +27,7 @@ export enum ColorScheme { } @observer -export class SettingsManager extends React.Component<{}> { +export class SettingsManager extends React.Component { // eslint-disable-next-line no-use-before-define public static Instance: SettingsManager; static _settingsStyle = addStyleSheet(); @@ -85,7 +85,7 @@ export class SettingsManager extends React.Component<{}> { if (this._playgroundMode) { DocServer.Control.makeReadOnly(); addStyleSheetRule(SettingsManager._settingsStyle, 'topbar-inner-container', { background: 'red !important' }); - } else ClientUtils.CurrentUserEmail() !== 'guest' && DocServer.Control.makeEditable(); + } else if (ClientUtils.CurrentUserEmail() !== 'guest') DocServer.Control.makeEditable(); }), 'set playgorund mode' ); @@ -121,7 +121,7 @@ export class SettingsManager extends React.Component<{}> { 'change color scheme' ); - constructor(props: {}) { + constructor(props: object) { super(props); makeObservable(this); SettingsManager.Instance = this; diff --git a/src/client/util/bezierFit.ts b/src/client/util/bezierFit.ts index 693676bc3..4c7f4a0ba 100644 --- a/src/client/util/bezierFit.ts +++ b/src/client/util/bezierFit.ts @@ -2,7 +2,6 @@ /* eslint-disable prefer-destructuring */ /* eslint-disable no-param-reassign */ /* eslint-disable camelcase */ -import e from 'cors'; import { Point } from '../../pen-gestures/ndollar'; export enum SVGType { @@ -628,7 +627,7 @@ export function GenerateControlPoints(coordinates: Point[], alpha = 0.1) { export function SVGToBezier(name: SVGType, attributes: any): Point[] { switch (name) { - case 'line': + case 'line': { const x1 = parseInt(attributes.x1); const x2 = parseInt(attributes.x2); const y1 = parseInt(attributes.y1); @@ -639,8 +638,9 @@ export function SVGToBezier(name: SVGType, attributes: any): Point[] { { X: x2, Y: y2 }, { X: x2, Y: y2 }, ]; + } case 'circle': - case 'ellipse': + case 'ellipse': { const c = 0.551915024494; const centerX = parseInt(attributes.cx); const centerY = parseInt(attributes.cy); @@ -664,7 +664,8 @@ export function SVGToBezier(name: SVGType, attributes: any): Point[] { { X: centerX - c * radiusX, Y: centerY + radiusY }, { X: centerX, Y: centerY + radiusY }, ]; - case 'rect': + } + case 'rect': { const x = parseInt(attributes.x); const y = parseInt(attributes.y); const width = parseInt(attributes.width); @@ -687,14 +688,15 @@ export function SVGToBezier(name: SVGType, attributes: any): Point[] { { X: x, Y: y }, { X: x, Y: y }, ]; - case 'path': + } + case 'path': { const coordList: Point[] = []; const startPt = attributes.d.match(/M(-?\d+\.?\d*),(-?\d+\.?\d*)/); coordList.push({ X: parseInt(startPt[1]), Y: parseInt(startPt[2]) }); const matches: RegExpMatchArray[] = Array.from( attributes.d.matchAll(/Q(-?\d+\.?\d*),(-?\d+\.?\d*) (-?\d+\.?\d*),(-?\d+\.?\d*)|C(-?\d+\.?\d*),(-?\d+\.?\d*) (-?\d+\.?\d*),(-?\d+\.?\d*) (-?\d+\.?\d*),(-?\d+\.?\d*)|L(-?\d+\.?\d*),(-?\d+\.?\d*)/g) ); - let lastPt: Point; + let lastPt: Point = { X: 0, Y: 0 }; matches.forEach(match => { if (match[0].startsWith('Q')) { coordList.push({ X: parseInt(match[1]), Y: parseInt(match[2]) }); @@ -725,7 +727,8 @@ export function SVGToBezier(name: SVGType, attributes: any): Point[] { coordList.pop(); } return coordList; - case 'polygon': + } + case 'polygon': { const coords: RegExpMatchArray[] = Array.from(attributes.points.matchAll(/(-?\d+\.?\d*),(-?\d+\.?\d*)/g)); let list: Point[] = []; coords.forEach(coord => { @@ -737,6 +740,7 @@ export function SVGToBezier(name: SVGType, attributes: any): Point[] { const firstPts = list.splice(0, 2); list = list.concat(firstPts); return list; + } default: return []; } diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 8aa4c2093..096f058ad 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -9,7 +9,7 @@ import * as React from 'react'; import { FaEdit } from 'react-icons/fa'; import { returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc, DocListCast } from '../../fields/Doc'; +import { Doc } from '../../fields/Doc'; import { Cast, DocCast } from '../../fields/Types'; import { DocUtils, IsFollowLinkScript } from '../documents/DocUtils'; import { CalendarManager } from '../util/CalendarManager'; @@ -17,7 +17,7 @@ import { DictationManager } from '../util/DictationManager'; import { DragManager } from '../util/DragManager'; import { dropActionType } from '../util/DropActionTypes'; import { SharingManager } from '../util/SharingManager'; -import { UndoManager, undoable, undoBatch } from '../util/UndoManager'; +import { UndoManager, undoable } from '../util/UndoManager'; import './DocumentButtonBar.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; import { PinProps } from './PinFuncs'; @@ -29,7 +29,6 @@ import { DocumentView } from './nodes/DocumentView'; import { OpenWhere } from './nodes/OpenWhere'; import { DashFieldView } from './nodes/formattedText/DashFieldView'; import { AnnotationPalette } from './smartdraw/AnnotationPalette'; -import { DocData } from '../../fields/DocSymbols'; @observer export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (DocumentView | undefined)[]; stack?: unknown }> { @@ -240,10 +239,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( ); } - @undoBatch - saveAnno = action(async (targetDoc: Doc) => { - await AnnotationPalette.addToPalette(targetDoc); - }); + saveAnno = undoable(async (targetDoc: Doc) => await AnnotationPalette.addToPalette(targetDoc), 'save to palette'); @computed get saveAnnoButton() { diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 63b472faf..befd19f6e 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -129,7 +129,6 @@ export class GestureOverlay extends ObservableReactComponent { - console.log('pointer up'); DocumentView.DownDocView = undefined; if (this._points.length > 1) { const B = this.svgBounds; @@ -145,9 +144,7 @@ export class GestureOverlay extends ObservableReactComponent 2 && GestureUtils.GestureRecognizer.Recognize([points]); - console.log(points); let actionPerformed = false; - console.log(result); if (Doc.UserDoc().recognizeGestures && result && result.Score > 0.7) { switch (result.Name) { case Gestures.Line: @@ -156,15 +153,16 @@ export class GestureOverlay extends ObservableReactComponent { + addPoints = undoable((inkView: DocumentView, t: number, i: number, controls: { X: number; Y: number }[]) => { this.applyFunction(inkView, (view: DocumentView /* , ink: InkData */) => { const doc = view.Document; const array = [controls[i], controls[i + 1], controls[i + 2], controls[i + 3]]; @@ -109,7 +108,7 @@ export class InkStrokeProperties { return controls; }); - }; + }, 'add ink points'); /** * Scales a handle point of a control point that is adjacent to a newly added one. @@ -164,46 +163,48 @@ export class InkStrokeProperties { /** * Deletes the current control point of the selected ink instance. */ - @undoBatch - deletePoints = (inkView: DocumentView, preserve: boolean) => - this.applyFunction( - inkView, - (view: DocumentView, ink: InkData) => { - const doc = view.Document; - const newPoints = ink.slice(); - const brokenIndices = NumListCast(doc.brokenInkIndices); - if (preserve || this._currentPoint === 0 || this._currentPoint === ink.length - 1 || brokenIndices.includes(this._currentPoint)) { - newPoints.splice(this._currentPoint === 0 ? 0 : this._currentPoint === ink.length - 1 ? this._currentPoint - 3 : this._currentPoint - 2, 4); - } else { - const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4; - const splicedPoints = ink.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8)); - const samples: Point[] = []; - let startDir = { x: 0, y: 0 }; - let endDir = { x: 0, y: 0 }; - for (let i = 0; i < splicedPoints.length / 4; i++) { - const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y }))); - if (i === 0) startDir = bez.derivative(0); - if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(1); - for (let t = 0; t < (i === splicedPoints.length / 4 - 1 ? 1 + 1e-7 : 1); t += 0.05) { - const pt = bez.compute(t); - samples.push(new Point(pt.x, pt.y)); - } - } - const { finalCtrls, error } = FitOneCurve(samples, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y }); - if (error < 100) { - newPoints.splice(this._currentPoint - 4, 8, ...finalCtrls); + deletePoints = undoable( + (inkView: DocumentView, preserve: boolean) => + this.applyFunction( + inkView, + (view: DocumentView, ink: InkData) => { + const doc = view.Document; + const newPoints = ink.slice(); + const brokenIndices = NumListCast(doc.brokenInkIndices); + if (preserve || this._currentPoint === 0 || this._currentPoint === ink.length - 1 || brokenIndices.includes(this._currentPoint)) { + newPoints.splice(this._currentPoint === 0 ? 0 : this._currentPoint === ink.length - 1 ? this._currentPoint - 3 : this._currentPoint - 2, 4); } else { - newPoints.splice(this._currentPoint - 2, 4); + const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4; + const splicedPoints = ink.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8)); + const samples: Point[] = []; + let startDir = { x: 0, y: 0 }; + let endDir = { x: 0, y: 0 }; + for (let i = 0; i < splicedPoints.length / 4; i++) { + const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y }))); + if (i === 0) startDir = bez.derivative(0); + if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(1); + for (let t = 0; t < (i === splicedPoints.length / 4 - 1 ? 1 + 1e-7 : 1); t += 0.05) { + const pt = bez.compute(t); + samples.push(new Point(pt.x, pt.y)); + } + } + const { finalCtrls, error } = FitOneCurve(samples, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y }); + if (error < 100) { + newPoints.splice(this._currentPoint - 4, 8, ...finalCtrls); + } else { + newPoints.splice(this._currentPoint - 2, 4); + } } - } - doc.brokenInkIndices = new List(brokenIndices.map(control => (control >= this._currentPoint ? control - 4 : control))); - runInAction(() => { - this._currentPoint = -1; - }); - return newPoints.length < 4 ? undefined : newPoints; - }, - true - ); + doc.brokenInkIndices = new List(brokenIndices.map(control => (control >= this._currentPoint ? control - 4 : control))); + runInAction(() => { + this._currentPoint = -1; + }); + return newPoints.length < 4 ? undefined : newPoints; + }, + true + ), + 'delete ink points' + ); /** * Rotates ink stroke(s) about a point @@ -211,8 +212,7 @@ export class InkStrokeProperties { * @param angle The angle at which to rotate the ink in radians. * @param scrpt The center point of the rotation in screen coordinates */ - @undoBatch - rotateInk = (inkStrokes: DocumentView[], angle: number, scrpt: PointData) => { + rotateInk = undoable((inkStrokes: DocumentView[], angle: number, scrpt: PointData) => { this.applyFunction(inkStrokes, (view: DocumentView, ink: InkData, xScale: number, yScale: number /* , inkStrokeWidth: number */) => { const inkCenterPt = view.ComponentView?.ptFromScreen?.(scrpt); return !inkCenterPt @@ -224,7 +224,7 @@ export class InkStrokeProperties { return { X: newX + inkCenterPt.X, Y: newY + inkCenterPt.Y }; }); }); - }; + }, 'rotate ink'); /** * Rotates ink stroke(s) about a point @@ -232,8 +232,7 @@ export class InkStrokeProperties { * @param angle The angle at which to rotate the ink in radians. * @param scrpt The center point of the rotation in screen coordinates */ - @undoBatch - stretchInk = (inkStrokes: DocumentView[], scaling: number, scrpt: PointData, scrVec: PointData, scaleUniformly: boolean) => { + stretchInk = undoable((inkStrokes: DocumentView[], scaling: number, scrpt: PointData, scrVec: PointData, scaleUniformly: boolean) => { this.applyFunction(inkStrokes, (view: DocumentView, ink: InkData) => { const ptFromScreen = view.ComponentView?.ptFromScreen; const ptToScreen = view.ComponentView?.ptToScreen; @@ -247,77 +246,79 @@ export class InkStrokeProperties { return ptFromScreen(newscrpt); }); }); - }; + }, 'stretch ink'); /** * Handles the movement/scaling of a control point. */ - @undoBatch - moveControlPtHandle = (inkView: DocumentView, deltaX: number, deltaY: number, controlIndex: number, origInk?: InkData) => - inkView && - this.applyFunction(inkView, (view: DocumentView, ink: InkData) => { - const order = controlIndex % 4; - const closed = InkingStroke.IsClosed(ink); - const brokenIndices = Cast(inkView.Document.brokenInkIndices, listSpec('number'), []); - if (origInk && this._currentPoint > 0 && this._currentPoint < ink.length - 1 && brokenIndices.findIndex(value => value === controlIndex) === -1) { - const cptBefore = ink[controlIndex]; - const cpt = { X: cptBefore.X + deltaX, Y: cptBefore.Y + deltaY }; - const newink = origInk.slice(); - const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4; - const splicedPoints = origInk.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8)); - const { nearestT, nearestSeg } = InkStrokeProperties.nearestPtToStroke(splicedPoints, cpt); - if ((nearestSeg === 0 && nearestT < 1e-1) || (nearestSeg === 4 && 1 - nearestT < 1e-1) || nearestSeg < 0) return ink.slice(); - const samplesLeft: Point[] = []; - const samplesRight: Point[] = []; - let startDir = { x: 0, y: 0 }; - let endDir = { x: 0, y: 0 }; - for (let i = 0; i < nearestSeg / 4 + 1; i++) { - const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y }))); - if (i === 0) startDir = bez.derivative(_.isEqual(bez.derivative(0), { x: 0, y: 0, t: 0 }) ? 1e-8 : 0); - if (i === nearestSeg / 4) endDir = bez.derivative(nearestT); - for (let t = 0; t < (i === nearestSeg / 4 ? nearestT + 0.05 : 1); t += 0.05) { - const pt = bez.compute(i !== nearestSeg / 4 ? t : Math.min(nearestT, t)); - samplesLeft.push(new Point(pt.x, pt.y)); + moveControlPtHandle = undoable( + (inkView: DocumentView, deltaX: number, deltaY: number, controlIndex: number, origInk?: InkData) => + inkView && + this.applyFunction(inkView, (view: DocumentView, ink: InkData) => { + const order = controlIndex % 4; + const closed = InkingStroke.IsClosed(ink); + const brokenIndices = Cast(inkView.Document.brokenInkIndices, listSpec('number'), []); + if (origInk && this._currentPoint > 0 && this._currentPoint < ink.length - 1 && brokenIndices.findIndex(value => value === controlIndex) === -1) { + const cptBefore = ink[controlIndex]; + const cpt = { X: cptBefore.X + deltaX, Y: cptBefore.Y + deltaY }; + const newink = origInk.slice(); + const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4; + const splicedPoints = origInk.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8)); + const { nearestT, nearestSeg } = InkStrokeProperties.nearestPtToStroke(splicedPoints, cpt); + if ((nearestSeg === 0 && nearestT < 1e-1) || (nearestSeg === 4 && 1 - nearestT < 1e-1) || nearestSeg < 0) return ink.slice(); + const samplesLeft: Point[] = []; + const samplesRight: Point[] = []; + let startDir = { x: 0, y: 0 }; + let endDir = { x: 0, y: 0 }; + for (let i = 0; i < nearestSeg / 4 + 1; i++) { + const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y }))); + if (i === 0) startDir = bez.derivative(_.isEqual(bez.derivative(0), { x: 0, y: 0, t: 0 }) ? 1e-8 : 0); + if (i === nearestSeg / 4) endDir = bez.derivative(nearestT); + for (let t = 0; t < (i === nearestSeg / 4 ? nearestT + 0.05 : 1); t += 0.05) { + const pt = bez.compute(i !== nearestSeg / 4 ? t : Math.min(nearestT, t)); + samplesLeft.push(new Point(pt.x, pt.y)); + } } - } - let { finalCtrls } = FitOneCurve(samplesLeft, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y }); - for (let i = nearestSeg / 4; i < splicedPoints.length / 4; i++) { - const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y }))); - if (i === nearestSeg / 4) startDir = bez.derivative(nearestT); - if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(_.isEqual(bez.derivative(1), { x: 0, y: 0, t: 1 }) ? 1 - 1e-8 : 1); - for (let t = i === nearestSeg / 4 ? nearestT : 0; t < (i === nearestSeg / 4 ? 1 + 0.05 + 1e-7 : 1 + 1e-7); t += 0.05) { - const pt = bez.compute(Math.min(1, t)); - samplesRight.push(new Point(pt.x, pt.y)); + let { finalCtrls } = FitOneCurve(samplesLeft, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y }); + for (let i = nearestSeg / 4; i < splicedPoints.length / 4; i++) { + const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y }))); + if (i === nearestSeg / 4) startDir = bez.derivative(nearestT); + if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(_.isEqual(bez.derivative(1), { x: 0, y: 0, t: 1 }) ? 1 - 1e-8 : 1); + for (let t = i === nearestSeg / 4 ? nearestT : 0; t < (i === nearestSeg / 4 ? 1 + 0.05 + 1e-7 : 1 + 1e-7); t += 0.05) { + const pt = bez.compute(Math.min(1, t)); + samplesRight.push(new Point(pt.x, pt.y)); + } } + const { finalCtrls: rightCtrls /* , error: errorRight */ } = FitOneCurve(samplesRight, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y }); + finalCtrls = finalCtrls.concat(rightCtrls); + newink.splice(this._currentPoint - 4, 8, ...finalCtrls); + return newink; } - const { finalCtrls: rightCtrls /* , error: errorRight */ } = FitOneCurve(samplesRight, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y }); - finalCtrls = finalCtrls.concat(rightCtrls); - newink.splice(this._currentPoint - 4, 8, ...finalCtrls); - return newink; - } - return ink.map((pt, i) => { - const leftHandlePoint = order === 0 && i === controlIndex + 1; - const rightHandlePoint = order === 0 && controlIndex !== 0 && i === controlIndex - 2; - if (controlIndex === i || (order === 0 && controlIndex !== 0 && i === controlIndex - 1) || (order === 3 && i === controlIndex - 1)) { - return { X: pt.X + deltaX, Y: pt.Y + deltaY }; - } - if ( - controlIndex === i || - leftHandlePoint || - rightHandlePoint || - (order === 0 && controlIndex !== 0 && i === controlIndex - 1) || - ((order === 0 || order === 3) && (controlIndex === 0 || controlIndex === ink.length - 1) && (i === 1 || i === ink.length - 2) && closed) || - (order === 3 && i === controlIndex - 1) || - (order === 3 && controlIndex !== ink.length - 1 && i === controlIndex + 1) || - (order === 3 && controlIndex !== ink.length - 1 && i === controlIndex + 2) || - (ink[0].X === ink[ink.length - 1].X && ink[0].Y === ink[ink.length - 1].Y && (i === 0 || i === ink.length - 1) && (controlIndex === 0 || controlIndex === ink.length - 1)) - ) { - return { X: pt.X + deltaX, Y: pt.Y + deltaY }; - } - return pt; - }); - }); + return ink.map((pt, i) => { + const leftHandlePoint = order === 0 && i === controlIndex + 1; + const rightHandlePoint = order === 0 && controlIndex !== 0 && i === controlIndex - 2; + if (controlIndex === i || (order === 0 && controlIndex !== 0 && i === controlIndex - 1) || (order === 3 && i === controlIndex - 1)) { + return { X: pt.X + deltaX, Y: pt.Y + deltaY }; + } + if ( + controlIndex === i || + leftHandlePoint || + rightHandlePoint || + (order === 0 && controlIndex !== 0 && i === controlIndex - 1) || + ((order === 0 || order === 3) && (controlIndex === 0 || controlIndex === ink.length - 1) && (i === 1 || i === ink.length - 2) && closed) || + (order === 3 && i === controlIndex - 1) || + (order === 3 && controlIndex !== ink.length - 1 && i === controlIndex + 1) || + (order === 3 && controlIndex !== ink.length - 1 && i === controlIndex + 2) || + (ink[0].X === ink[ink.length - 1].X && ink[0].Y === ink[ink.length - 1].Y && (i === 0 || i === ink.length - 1) && (controlIndex === 0 || controlIndex === ink.length - 1)) + ) { + return { X: pt.X + deltaX, Y: pt.Y + deltaY }; + } + return pt; + }); + }), + 'move ink ctrl pt' + ); public static nearestPtToStroke(ctrlPoints: { X: number; Y: number }[], refInkSpacePt: { X: number; Y: number }, excludeSegs?: number[]) { let distance = Number.MAX_SAFE_INTEGER; @@ -470,26 +471,28 @@ export class InkStrokeProperties { /** * Handles the movement/scaling of a handle point. */ - @undoBatch - moveTangentHandle = (inkView: DocumentView, deltaX: number, deltaY: number, handleIndex: number, oppositeHandleIndex: number, controlIndex: number) => - this.applyFunction(inkView, (view: DocumentView, ink: InkData) => { - const doc = view.Document; - const closed = InkingStroke.IsClosed(ink); - const oldHandlePoint = ink[handleIndex]; - const oppositeHandlePoint = ink[oppositeHandleIndex]; - const controlPoint = ink[controlIndex]; - const newHandlePoint = { X: ink[handleIndex].X - deltaX, Y: ink[handleIndex].Y - deltaY }; - const inkCopy = ink.slice(); - inkCopy[handleIndex] = newHandlePoint; - const brokenIndices = Cast(doc.brokenInkIndices, listSpec('number')); - const equivIndex = closed ? (controlIndex === 0 ? ink.length - 1 : controlIndex === ink.length - 1 ? 0 : -1) : -1; - // Rotate opposite handle if user hasn't held 'Alt' key or not first/final control (which have only 1 handle). - if ((!brokenIndices || (!brokenIndices?.includes(controlIndex) && !brokenIndices?.includes(equivIndex))) && (closed || (handleIndex !== 1 && handleIndex !== ink.length - 2))) { - const angle = InkStrokeProperties.angleChange(oldHandlePoint, newHandlePoint, controlPoint); - inkCopy[oppositeHandleIndex] = this.rotatePoint(oppositeHandlePoint, controlPoint, angle); - } - return inkCopy; - }); + moveTangentHandle = undoable( + (inkView: DocumentView, deltaX: number, deltaY: number, handleIndex: number, oppositeHandleIndex: number, controlIndex: number) => + this.applyFunction(inkView, (view: DocumentView, ink: InkData) => { + const doc = view.Document; + const closed = InkingStroke.IsClosed(ink); + const oldHandlePoint = ink[handleIndex]; + const oppositeHandlePoint = ink[oppositeHandleIndex]; + const controlPoint = ink[controlIndex]; + const newHandlePoint = { X: ink[handleIndex].X - deltaX, Y: ink[handleIndex].Y - deltaY }; + const inkCopy = ink.slice(); + inkCopy[handleIndex] = newHandlePoint; + const brokenIndices = Cast(doc.brokenInkIndices, listSpec('number')); + const equivIndex = closed ? (controlIndex === 0 ? ink.length - 1 : controlIndex === ink.length - 1 ? 0 : -1) : -1; + // Rotate opposite handle if user hasn't held 'Alt' key or not first/final control (which have only 1 handle). + if ((!brokenIndices || (!brokenIndices?.includes(controlIndex) && !brokenIndices?.includes(equivIndex))) && (closed || (handleIndex !== 1 && handleIndex !== ink.length - 2))) { + const angle = InkStrokeProperties.angleChange(oldHandlePoint, newHandlePoint, controlPoint); + inkCopy[oppositeHandleIndex] = this.rotatePoint(oppositeHandlePoint, controlPoint, angle); + } + return inkCopy; + }), + 'move ink tangent' + ); /** * Function that "smooths" ink strokes by using the gesture recognizer to detect shapes and @@ -497,8 +500,7 @@ export class InkStrokeProperties { * @param inkDocs * @param tolerance Determines how strong the smooth effect will be */ - @undoBatch - smoothInkStrokes = (inkDocs: Doc[], tolerance: number = 5) => { + smoothInkStrokes = undoable((inkDocs: Doc[], tolerance: number = 5) => { inkDocs.forEach(inkDoc => { const inkView = DocumentView.getDocumentView(inkDoc); const inkStroke = inkView?.ComponentView as InkingStroke; @@ -530,5 +532,5 @@ export class InkStrokeProperties { } } }); - }; + }, 'smooth ink stroke'); } diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index b94041642..f346d4ba8 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -13,7 +13,7 @@ import { MdClosedCaption, MdClosedCaptionDisabled, MdGridOff, MdGridOn, MdSubtit import { RxWidth } from 'react-icons/rx'; import { TbEditCircle, TbEditCircleOff, TbHandOff, TbHandStop, TbHighlight, TbHighlightOff } from 'react-icons/tb'; import { TfiBarChart } from 'react-icons/tfi'; -import { Doc, DocListCast, Opt } from '../../fields/Doc'; +import { Doc, Opt } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { ScriptField } from '../../fields/ScriptField'; import { BoolCast, ScriptCast, StrCast } from '../../fields/Types'; @@ -28,8 +28,6 @@ import { Colors } from './global/globalEnums'; import { DocumentView } from './nodes/DocumentView'; import { OpenWhere } from './nodes/OpenWhere'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; -import { MarqueeOptionsMenu } from './collections/collectionFreeForm'; -import { InkStrokeProperties } from './InkStrokeProperties'; @observer export class PropertiesButtons extends React.Component { diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 98ddc61f9..486c826b6 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -847,7 +847,7 @@ class StackedTimelineAnchor extends ObservableReactComponent>() { public static AddTreeFunc = 'addTreeFolder(this.embedContainer)'; private _treedropDisposer?: DragManager.DragDropDisposer; - private _mainEle?: HTMLDivElement; private _titleRef?: HTMLDivElement | HTMLInputElement | null; private _disposers: { [name: string]: IReactionDisposer } = {}; private _isDisposing = false; // notes that instance is in process of being disposed @@ -81,8 +80,6 @@ export class CollectionTreeView extends CollectionSubView this._mainEle; - // these should stay in synch with counterparts in DocComponent.ts ViewBoxAnnotatableComponent @observable _isAnyChildContentActive = false; whenChildContentsActiveChanged = action((isActive: boolean) => { @@ -132,7 +129,6 @@ export class CollectionTreeView extends CollectionSubView { this._treedropDisposer?.(); - this._mainEle = ele; if (ele) this._treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.Document, this.onInternalPreDrop.bind(this)); }; diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 847ff5491..015f77ffd 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -472,7 +472,7 @@ export class TreeView extends ObservableReactComponent { const { translateX, translateY, scale } = ClientUtils.GetScreenTransform(ref); return new Transform(-translateX, -translateY, 1).scale(1 / scale); }; - docTransform = () => this.refTransform(this._dref?.ContentRef?.current); + docTransform = () => this.refTransform(this._dref?.ContentDiv); getTransform = () => this.refTransform(this._tref.current); embeddedPanelWidth = () => this._props.panelWidth() / (this.treeView._props.NativeDimScaling?.() || 1); embeddedPanelHeight = () => { @@ -754,7 +754,7 @@ export class TreeView extends ObservableReactComponent { } get onCheckedClick() { - return this.Document.type === DocumentType.COL ? undefined : this._props.onCheckedClick?.() ?? ScriptCast(this.Document.onCheckedClick); + return this.Document.type === DocumentType.COL ? undefined : (this._props.onCheckedClick?.() ?? ScriptCast(this.Document.onCheckedClick)); } @action @@ -779,7 +779,7 @@ export class TreeView extends ObservableReactComponent { TraceMobx(); const iconType = (this.treeView._props.styleProvider?.(this.Document, this.treeView._props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ':treeOpen' : !this.childDocs.length ? ':empty' : '')) as string) || 'question'; const color = SettingsManager.userColor; - const checked = this.onCheckedClick ? this.Document.treeView_Checked ?? 'unchecked' : undefined; + const checked = this.onCheckedClick ? (this.Document.treeView_Checked ?? 'unchecked') : undefined; return (
- ); + )); }; render() { @@ -994,7 +994,7 @@ export class DocumentViewInternal extends DocComponent() { finished?: (changed: boolean) => void // func called after focusing on target with flag indicating whether anything needed to be done. ) => Promise; public static linkCommonAncestor: (link: Doc) => DocumentView | undefined; - // pin func + /** + * Pins a Doc to the current presentation trail. (see TabDocView for implementation) + */ public static PinDoc: (docIn: Doc | Doc[], pinProps: PinProps) => void; - // gesture + /** + * The DocumentView below the cursor at the start of a gesture (that receives the pointerDown event). Used by GestureOverlay to determine the doc a gesture should apply to. + */ public static DownDocView: DocumentView | undefined; // the first DocView that receives a pointerdown event. used by GestureOverlay to determine the doc a gesture should apply to. public get displayName() { return 'DocumentView(' + (this.Document?.title??"") + ')'; } // prettier-ignore - public ContentRef = React.createRef(); private _htmlOverlayEffect: Opt; private _disposers: { [name: string]: IReactionDisposer } = {}; private _viewTimer: NodeJS.Timeout | undefined; @@ -1468,11 +1471,10 @@ export class DocumentView extends DocComponent() { {!this.Document || !this._props.PanelWidth() ? null : (
(['Audio Tags', 'Text from Others', 'Todo Items', 'Important Items', 'Disagree Items', 'Ignore Items']); - static _highlightStyleSheet: any = addStyleSheet(); - static _bulletStyleSheet: any = addStyleSheet(); - static _userStyleSheet: any = addStyleSheet(); + static _highlightStyleSheet = addStyleSheet(); + static _bulletStyleSheet = addStyleSheet(); + static _userStyleSheet = addStyleSheet(); static _hadSelection: boolean = false; private _oldWheel: HTMLDivElement | null = null; private _selectionHTML: string | undefined; @@ -361,7 +361,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent ) : ( -
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => DocumentView.SelectView(this.DocumentView?.()!, false), true)}> +
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => DocumentView.SelectView(this.DocumentView?.(), false), true)}> { outline = () => (this.linkHighlighted ? 'solid 1px lightBlue' : undefined); background = () => (this._props.annoDoc[Highlight] ? 'orange' : StrCast(this._props.annoDoc.backgroundColor)); render() { + const forceRenderHack = [this.background(), this.outline(), this.opacity()]; // forces a re-render when these change -- because RegionAnnotation doesn't do this internally.. return (
{StrListCast(this._props.annoDoc.text_inlineAnnotations) diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 5984905d0..20719442a 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -52,7 +52,7 @@ interface IViewerProps extends FieldViewProps { */ @observer export class PDFViewer extends ObservableReactComponent { - static _annotationStyle: any = addStyleSheet(); + static _annotationStyle = addStyleSheet(); constructor(props: IViewerProps) { super(props); @@ -522,7 +522,7 @@ export class PDFViewer extends ObservableReactComponent { if (doc instanceof Doc && property === StyleProp.PointerEvents) { if (this.inlineTextAnnotations.includes(doc) || this._props.isContentActive() === false) return 'none'; const isInk = doc.layout_isSvg && !props?.LayoutTemplateString; - return isInk ? 'visiblePainted' : 'all'; + if (isInk) return 'visiblePainted'; } return this._props.styleProvider?.(doc, props, property); }; diff --git a/src/client/views/smartdraw/AnnotationPalette.tsx b/src/client/views/smartdraw/AnnotationPalette.tsx index c296138a8..22b8b7b67 100644 --- a/src/client/views/smartdraw/AnnotationPalette.tsx +++ b/src/client/views/smartdraw/AnnotationPalette.tsx @@ -6,25 +6,25 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { AiOutlineSend } from 'react-icons/ai'; import ReactLoading from 'react-loading'; -import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils'; -import { Doc, DocListCast } from '../../../fields/Doc'; +import { returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils'; +import { emptyFunction } from '../../../Utils'; +import { Doc, DocListCast, returnEmptyDoclist } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; +import { Copy } from '../../../fields/FieldSymbols'; import { ImageCast } from '../../../fields/Types'; -import { emptyFunction } from '../../../Utils'; +import { ImageField } from '../../../fields/URLField'; +import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { makeUserTemplateImage } from '../../util/DropConverter'; import { SettingsManager } from '../../util/SettingsManager'; import { Transform } from '../../util/Transform'; import { undoBatch } from '../../util/UndoManager'; +import { ObservableReactComponent } from '../ObservableReactComponent'; +import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider'; import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView'; import { FieldView } from '../nodes/FieldView'; -import { ObservableReactComponent } from '../ObservableReactComponent'; -import { DefaultStyleProvider } from '../StyleProvider'; import './AnnotationPalette.scss'; import { DrawingOptions, SmartDrawHandler } from './SmartDrawHandler'; -import { DocumentType } from '../../documents/DocumentTypes'; -import { ImageField } from '../../../fields/URLField'; -import { Copy } from '../../../fields/FieldSymbols'; interface AnnotationPaletteProps { Document: Doc; @@ -204,7 +204,7 @@ export class AnnotationPalette extends ObservableReactComponent { const svgStrokes: INode[] = svgObject.children; const strokeData: [InkData, string, string][] = []; // eslint-disable-next-line @typescript-eslint/no-explicit-any - svgStrokes.forEach((child: any) => { + svgStrokes.forEach((child) => { const convertedBezier: InkData = SVGToBezier(child.name, child.attributes); strokeData.push([ convertedBezier.map(point => { -- cgit v1.2.3-70-g09d2 From 19f0184da33b5080f301baf78cf8e6f6d113d79a Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 1 Oct 2024 14:03:00 -0400 Subject: fixed some lint errors. fixed how drawings are added to pdfs. fixed testing for ink in propertiesView. --- src/client/views/PropertiesView.tsx | 23 ++++++++++------------- src/client/views/pdf/PDFViewer.tsx | 2 +- 2 files changed, 11 insertions(+), 14 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index fcac4d464..722086aea 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -18,11 +18,12 @@ import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; import { List } from '../../fields/List'; import { ComputedField } from '../../fields/ScriptField'; -import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; +import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { GetEffectiveAcl, SharingPermissions, normalizeEmail } from '../../fields/util'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { GroupManager } from '../util/GroupManager'; import { LinkManager } from '../util/LinkManager'; +import { SettingsManager } from '../util/SettingsManager'; import { SharingManager } from '../util/SharingManager'; import { SnappingManager } from '../util/SnappingManager'; import { Transform } from '../util/Transform'; @@ -30,6 +31,7 @@ import { UndoManager, undoBatch, undoable } from '../util/UndoManager'; import { EditableView } from './EditableView'; import { FilterPanel } from './FilterPanel'; import { InkStrokeProperties } from './InkStrokeProperties'; +import { InkingStroke } from './InkingStroke'; import { ObservableReactComponent } from './ObservableReactComponent'; import { PropertiesButtons } from './PropertiesButtons'; import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector'; @@ -41,9 +43,6 @@ import { DocumentView } from './nodes/DocumentView'; import { StyleProviderFuncType } from './nodes/FieldView'; import { OpenWhere } from './nodes/OpenWhere'; import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails'; -import { InkingStroke } from './InkingStroke'; -import { SettingsManager } from '../util/SettingsManager'; -import { MarqueeOptionsMenu } from './collections/collectionFreeForm'; import { SmartDrawHandler } from './smartdraw/SmartDrawHandler'; interface PropertiesViewProps { @@ -75,6 +74,10 @@ export class PropertiesView extends ObservableReactComponent { this.openFilters = bool; })} onDoubleClick={this.CloseAll}>
- ); + ); // prettier-ignore } @computed get inkSubMenu() { - this.containsInkDoc = false; return ( - // prettier-ignore <> { this.openAppearance = bool; }} onDoubleClick={this.CloseAll}> {this.selectedLayoutDoc?.layout_isSvg ? this.appearanceEditor : null} @@ -1297,7 +1296,7 @@ export class PropertiesView extends ObservableReactComponent - ); + ); // prettier-ignore } /** @@ -1306,14 +1305,12 @@ export class PropertiesView extends ObservableReactComponent { const childDocs: Doc[] = DocListCast(selectedDoc[DocData].data); - for (var i = 0; i < childDocs.length; i++) { + for (let i = 0; i < childDocs.length; i++) { if (DocumentView.getDocumentView(childDocs[i])?.layoutDoc?.layout_isSvg) { this.inkDoc = childDocs[i]; - this.containsInkDoc = true; return true; } } - this.containsInkDoc = false; return false; }; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 20719442a..01242ba48 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -430,7 +430,7 @@ export class PDFViewer extends ObservableReactComponent { addDrawingAnnotation = (drawing: Doc) => { // drawing[DocData].x = this._props.pdfBox.ScreenToLocalBoxXf().TranslateX // const scaleX = this._mainCont.current.offsetWidth / boundingRect.width; - drawing.y = (drawing.y as number) + (this._props.Document.data_sidebar_panY as number); + drawing.y = NumCast(drawing.y) + NumCast(this._props.Document.layout_scrollTop); this._props.addDocument?.(drawing); }; -- cgit v1.2.3-70-g09d2 From 8d7bf0588ca63c2d505a494865dcf9921eb1383d Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 1 Oct 2024 15:57:57 -0400 Subject: more lint fixes --- src/client/util/CurrentUserUtils.ts | 2 +- src/client/util/DropConverter.ts | 2 +- src/client/util/Scripting.ts | 5 ----- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/InkTranscription.tsx | 1 + src/client/views/InkingStroke.tsx | 4 +--- src/client/views/LightboxView.tsx | 2 -- src/client/views/Main.tsx | 1 - src/client/views/MainView.tsx | 19 +++++++------------ .../views/collections/CollectionCarousel3DView.tsx | 2 +- .../views/collections/CollectionCarouselView.tsx | 4 ++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 11 ++++++----- .../collections/collectionFreeForm/MarqueeView.tsx | 3 +-- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 1 - src/client/views/nodes/ImageBox.tsx | 1 - src/client/views/nodes/PDFBox.tsx | 5 +---- .../views/nodes/formattedText/FormattedTextBox.tsx | 8 -------- src/client/views/nodes/formattedText/RichTextMenu.tsx | 1 - src/client/views/pdf/PDFViewer.tsx | 2 -- src/client/views/smartdraw/AnnotationPalette.tsx | 4 +--- src/client/views/smartdraw/SmartDrawHandler.tsx | 3 ++- src/fields/SchemaHeaderField.ts | 1 - 22 files changed, 26 insertions(+), 58 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 59760ba32..96d69e7a1 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -2,7 +2,7 @@ import { reaction, runInAction } from "mobx"; import * as rp from 'request-promise'; import { ClientUtils, OmitKeys } from "../../ClientUtils"; -import { Doc, DocListCast, DocListCastAsync, FieldType, Opt, StrListCast } from "../../fields/Doc"; +import { Doc, DocListCast, DocListCastAsync, FieldType, Opt } from "../../fields/Doc"; import { DocData } from "../../fields/DocSymbols"; import { InkTool } from "../../fields/InkField"; import { List } from "../../fields/List"; diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 71cdaa58b..b5d29be4c 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -67,7 +67,7 @@ export function MakeTemplate(doc: Doc) { /** * Makes a draggable button or image that will create a template doc Instance */ -export function makeUserTemplateButtonOrImage(doc: Doc, image: string | undefined) { +export function makeUserTemplateButtonOrImage(doc: Doc, image?: string) { const layoutDoc = doc; // doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; if (layoutDoc.type !== DocumentType.FONTICON) { !layoutDoc.isTemplateDoc && makeTemplate(layoutDoc); diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index c63d3d7cb..c7b86815a 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -60,7 +60,6 @@ function Run(script: string | undefined, customParams: string[], diagnostics: ts // let params: any[] = [Docs, ...fieldTypes]; const compiledFunction = (() => { try { - // eslint-disable-next-line no-new-func return new Function(...paramNames, `return ${script}`); } catch (e) { console.log(e); @@ -69,10 +68,8 @@ function Run(script: string | undefined, customParams: string[], diagnostics: ts })(); if (!compiledFunction) return { compiled: false, errors }; const { capturedVariables = {} } = options; - // eslint-disable-next-line default-param-last const run = (args: { [name: string]: unknown } = {}, onError?: (e: string) => void, errorVal?: ts.Diagnostic): ScriptResult => { const argsArray: unknown[] = []; - // eslint-disable-next-line no-restricted-syntax for (const name of customParams) { if (name !== 'this') { argsArray.push(name in args ? args[name] : capturedVariables[name]); @@ -224,7 +221,6 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp if ('this' in params || 'this' in capturedVariables) { paramNames.push('this'); } - // eslint-disable-next-line no-restricted-syntax for (const key in params) { if (key !== 'this') { paramNames.push(key); @@ -234,7 +230,6 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp const val = params[key]; return `${key}: ${val}`; }); - // eslint-disable-next-line no-restricted-syntax for (const key in capturedVariables) { if (key !== 'this') { const val = capturedVariables[key]; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index e569c7dc8..1c0d51e17 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -2,7 +2,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; import { IconButton } from 'browndash-components'; -import { action, computed, makeObservable, observable, runInAction, trace } from 'mobx'; +import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { FaUndo } from 'react-icons/fa'; diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx index b4f4ebf63..24d53a8c8 100644 --- a/src/client/views/InkTranscription.tsx +++ b/src/client/views/InkTranscription.tsx @@ -18,6 +18,7 @@ import { URLField } from '../../fields/URLField'; * Class component that handles inking in writing mode */ export class InkTranscription extends React.Component { + // eslint-disable-next-line no-use-before-define static Instance: InkTranscription; // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index f44c1720d..270266a94 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -51,7 +51,7 @@ import { InkTranscription } from './InkTranscription'; import { CollectionFreeFormView } from './collections/collectionFreeForm'; import { DocumentView } from './nodes/DocumentView'; -// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports +// eslint-disable-next-line @typescript-eslint/no-require-imports const { INK_MASK_SIZE } = require('./global/globalCssVariables.module.scss'); // prettier-ignore @observer @@ -473,7 +473,6 @@ export class InkingStroke extends ViewBoxAnnotatableComponent() // mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset', cursor: this._props.isSelected() ? 'default' : undefined, }} - // eslint-disable-next-line react/jsx-props-no-spreading {...interactions}> {clickableLine(this.onPointerDown, isInkMask)} {isInkMask ? null : inkLine} @@ -490,7 +489,6 @@ export class InkingStroke extends ViewBoxAnnotatableComponent() // top: (this._props.PanelHeight() - (lineHeightGuess * fsize + 20) * (this._props.NativeDimScaling?.() || 1)) / 2, }}> { ); } // prettier-ignore public static LightboxDoc = () => LightboxView.Instance?._doc; - // eslint-disable-next-line no-use-before-define static Instance: LightboxView; private _path: { doc: Opt; // @@ -341,7 +340,6 @@ export class LightboxView extends ObservableReactComponent { } interface LightboxTourBtnProps { navBtn: (left: Opt, bottom: Opt, top: number, icon: IconProp, display: boolean, click: () => void, color?: string) => JSX.Element; - // eslint-disable-next-line react/no-unused-prop-types future: () => Opt; stepInto: () => void; lightboxDoc: () => Opt; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 94294e97a..73d2872d1 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-new */ // if ((module as any).hot) { // (module as any).hot.accept(); // } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 8e921ca5e..fa2e94d12 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -3,7 +3,7 @@ import { faBuffer, faHireAHelper } from '@fortawesome/free-brands-svg-icons'; import * as far from '@fortawesome/free-regular-svg-icons'; import * as fa from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, configure, makeObservable, observable, reaction, runInAction, trace } from 'mobx'; +import { action, computed, configure, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import ResizeObserver from 'resize-observer-polyfill'; @@ -77,7 +77,7 @@ import { TopBar } from './topbar/TopBar'; import { SmartDrawHandler } from './smartdraw/SmartDrawHandler'; import { InkTranscription } from './InkTranscription'; -// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports +// eslint-disable-next-line @typescript-eslint/no-require-imports const { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } = require('./global/globalCssVariables.module.scss'); // prettier-ignore @observer @@ -977,13 +977,11 @@ export class MainView extends ObservableReactComponent {
{[ - ...SnappingManager.HorizSnapLines.map((l, i) => ( - // eslint-disable-next-line react/no-array-index-key - + ...SnappingManager.HorizSnapLines.map(l => ( + )), - ...SnappingManager.VertSnapLines.map((l, i) => ( - // eslint-disable-next-line react/no-array-index-key - + ...SnappingManager.VertSnapLines.map(l => ( + )), ]} @@ -1071,10 +1069,7 @@ export class MainView extends ObservableReactComponent { docView={DocButtonState.Instance.LinkEditorDocView} /> ) : null} - {LinkInfo.Instance?.LinkInfo ? ( - // eslint-disable-next-line react/jsx-props-no-spreading - - ) : null} + {LinkInfo.Instance?.LinkInfo ? : null} {((page: string) => { // prettier-ignore switch (page) { diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index cf86a0a4e..c5da8e037 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -16,6 +16,7 @@ import './CollectionCarousel3DView.scss'; import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; import { Transform } from '../../util/Transform'; +// eslint-disable-next-line @typescript-eslint/no-require-imports const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = require('../global/globalCssVariables.module.scss'); @observer @@ -88,7 +89,6 @@ export class CollectionCarousel3DView extends CollectionSubView() { const currentIndex = NumCast(this.layoutDoc._carousel_index); const displayDoc = (childPair: { layout: Doc; data: Doc }, dxf: () => Transform) => ( this.showSmartDraw(e.pageX, e.pageY), hit !== -1); + setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, () => this.showSmartDraw(e.pageX, e.pageY), hit !== -1); e.stopPropagation(); + break; 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); + const ahit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY)); + setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, ahit !== -1, false); } break; default: @@ -655,13 +656,13 @@ export class CollectionFreeFormView extends CollectionSubView { + onEraserClick = (e: PointerEvent) => { e.preventDefault(); e.stopImmediatePropagation(); this.erase(e, [0, 0]); }; - forceStrokeGesture = (e: PointerEvent, gesture: Gestures, points: InkData, text?: any) => { + forceStrokeGesture = (e: PointerEvent, gesture: Gestures, points: InkData, text?: string) => { this.onGesture(e, new GestureUtils.GestureEvent(gesture, points, InkField.getBounds(points), text)); }; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 7614b5337..b1f6815b3 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -57,6 +57,7 @@ export class MarqueeView extends ObservableReactComponent tl[0] && truePoint[0] < r1.left && truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height); diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index b7e1350ca..feaf84b7b 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/jsx-props-no-spreading */ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Button, ColorPicker, Dropdown, DropdownType, IconButton, IListItemProps, MultiToggle, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components'; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index aa4376bb2..ec5e062c8 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -493,7 +493,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { }}> () { return ComponentTag === CollectionStackingView ? ( () { ) : (
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this._props.select(false), true)}> () { top: 0, }}> -1) { const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + blockOffset + foundAt + 1), pm.state.doc.resolve(ep.from + index + blockOffset + foundAt + find.length + 1)); ret.push(sel); @@ -714,7 +713,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-hr-' + (hr - i), { opacity: ((10 - i - 1) / 10).toString() })); } - // eslint-disable-next-line operator-assignment this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css changes happen outside of react/mobx. so we need to set a flag that will notify anyone interested in layout changes triggered by css changes (eg., CollectionLinkView) }; @@ -1131,7 +1129,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - // eslint-disable-next-line no-use-before-define const examinedNode = findAnchorNode(node, editor); if (examinedNode?.node && (examinedNode.node.textContent || examinedNode.node.type === this._editorView?.state.schema.nodes.dashDoc || examinedNode.node.type === this._editorView?.state.schema.nodes.audiotag)) { nodes.push(examinedNode.node); @@ -1285,7 +1282,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { this.prepareForTyping(); if (FormattedTextBox._globalHighlights.has('Bold Text')) { - // eslint-disable-next-line operator-assignment this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed } if (RichTextMenu.Instance?.view === this._editorView && !selected) { @@ -1711,7 +1707,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent kids?.reduce((p, child) => p + toHgt(child), margins) ?? 0; const toNum = (val: string) => Number(val.replace('px', '')); const toHgt = (node: Element): number => { @@ -1880,7 +1874,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => DocumentView.SelectView(this.DocumentView?.(), false), true)}> { - // eslint-disable-next-line react/no-unused-class-component-methods update(view: EditorView & { TextView?: FormattedTextBox }, lastState: EditorState | undefined) { RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps, view.TextView?.layoutDoc); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 01242ba48..7a86ee802 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -504,7 +504,6 @@ export class PDFViewer extends ObservableReactComponent { return (
{inlineAnnos.map(anno => ( - // eslint-disable-next-line react/jsx-props-no-spreading ))}
@@ -537,7 +536,6 @@ export class PDFViewer extends ObservableReactComponent { pointerEvents: Doc.ActiveTool !== InkTool.None ? 'all' : undefined, }}> { + // eslint-disable-next-line no-use-before-define static Instance: SmartDrawHandler; private _lastInput: DrawingOptions = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 }; @@ -395,7 +396,7 @@ export class SmartDrawHandler extends ObservableReactComponent { defaultChecked={true} value={this._autoColor} size="small" - onChange={action(e => this._canInteract && (this._autoColor = !this._autoColor))} + onChange={action(() => this._canInteract && (this._autoColor = !this._autoColor))} />
diff --git a/src/fields/SchemaHeaderField.ts b/src/fields/SchemaHeaderField.ts index 0a8dd1d9e..5f4d59cf9 100644 --- a/src/fields/SchemaHeaderField.ts +++ b/src/fields/SchemaHeaderField.ts @@ -79,7 +79,6 @@ export class SchemaHeaderField extends ObjectField { @serializable(primitive()) desc: boolean | undefined; // boolean determines sort order, undefined when no sort - // eslint-disable-next-line default-param-last constructor(heading: string = '', color: string = RandomPastel(), type?: ColumnType, width?: number, desc?: boolean, collapsed?: boolean) { super(); -- cgit v1.2.3-70-g09d2