aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx11
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss78
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx287
-rw-r--r--src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/ImageLabelHandler.tsx16
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx9
11 files changed, 287 insertions, 134 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
index 8530f7a23..3838852dd 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
@@ -186,13 +186,13 @@ export class CollectionFreeFormClusters {
case StyleProp.BackgroundColor:
{
const cluster = NumCast(doc?.layout_cluster);
- if (this.Document._freeform_useClusters && doc?.type !== DocumentType.IMG) {
+ if (this.Document._freeform_useClusters && doc?.type !== DocumentType.IMG && !doc.layout_isSvg) {
if (this._clusterSets.length <= cluster) {
setTimeout(() => doc && this.addDocument(doc));
} else {
const palette = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)'];
- // override palette cluster color with an explicitly set cluster doc color
- return this._clusterSets[cluster]?.reduce((b, s) => StrCast(s.backgroundColor, b), palette[cluster % palette.length]);
+ // override palette cluster color with an explicitly set cluster doc color ONLY if doc color matches the current default text color
+ return this._clusterSets[cluster]?.reduce((b, s) => (s.backgroundColor !== Doc.UserDoc().textBackgroundColor ? StrCast(s.backgroundColor, b) : b), palette[cluster % palette.length]);
}
}
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
index 51add85a8..437888ef2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
@@ -1,4 +1,4 @@
-import { IconButton, Size, Type } from 'browndash-components';
+import { IconButton, Size, Type } from '@dash/components';
import { IReactionDisposer, action, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
index 5d8373fc7..8b9a3e0ec 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
@@ -29,7 +29,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
}
_firstDocPos = { x: 0, y: 0 };
- constructor(props: any) {
+ constructor(props: CollectionFreeFormInfoUIProps) {
super(props);
makeObservable(this);
this._currState = this.setupStates();
@@ -163,7 +163,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
return presentDocs;
}],
// eslint-disable-next-line no-use-before-define
- activePen: [() => activeTool() === InkTool.Pen, () => penMode],
+ activePen: [() => activeTool() === InkTool.Ink, () => penMode],
},
'documentation.png',
() => TopBar.Instance.FlipDocumentationIcon()
@@ -187,7 +187,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
const penMode = InfoState('You\'re in pen mode. Click and drag to draw your first masterpiece.', {
// activePen: [() => activeTool() === InkTool.Eraser, () => eraserMode],
- activePen: [() => activeTool() !== InkTool.Pen, () => viewedLink],
+ activePen: [() => activeTool() !== InkTool.Ink, () => viewedLink],
}); // prettier-ignore
// const eraserMode = InfoState('You\'re in eraser mode. Say goodbye to your first masterpiece.', {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 79aad0ef2..241a56a88 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -4,7 +4,7 @@ import { Id, ToString } from '../../../../fields/FieldSymbols';
import { ObjectField } from '../../../../fields/ObjectField';
import { RefField } from '../../../../fields/RefField';
import { listSpec } from '../../../../fields/Schema';
-import { Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { Cast, DateCast, NumCast, StrCast } from '../../../../fields/Types';
import { aggregateBounds } from '../../../../Utils';
export interface ViewDefBounds {
@@ -44,6 +44,7 @@ export interface PoolData {
transition?: string;
highlight?: boolean;
pointerEvents?: string;
+ showTags?: boolean;
}
export interface ViewDefResult {
@@ -102,12 +103,11 @@ export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc
replica: '',
});
});
- // eslint-disable-next-line no-use-before-define
return normalizeResults(panelDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []);
}
function toNumber(val: FieldResult<FieldType>) {
- return val === undefined ? undefined : NumCast(val, Number(StrCast(val)));
+ return val === undefined ? undefined : DateCast(val) ? DateCast(val).date.getMilliseconds() : NumCast(val, Number(StrCast(val)));
}
export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] /* , engineProps: any */) {
@@ -272,7 +272,6 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
payload: pivotColumnGroups.get(key)?.filters,
}));
groupNames.push(...dividers);
- // eslint-disable-next-line no-use-before-define
return normalizeResults(panelDim, maxText, docMap, poolData, viewDefsToJSX, groupNames, 0, []);
}
@@ -296,7 +295,7 @@ export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc:
let minTime = minTimeReq === undefined ? Number.MAX_VALUE : minTimeReq;
let maxTime = maxTimeReq === undefined ? -Number.MAX_VALUE : maxTimeReq;
childPairs.forEach(pair => {
- const num = NumCast(pair.layout[timelineFieldKey], Number(StrCast(pair.layout[timelineFieldKey])));
+ const num = toNumber(pair.layout[timelineFieldKey]) ?? 0;
if (!isNaN(num) && (!minTimeReq || num >= minTimeReq) && (!maxTimeReq || num <= maxTimeReq)) {
!pivotDateGroups.get(num) && pivotDateGroups.set(num, []);
pivotDateGroups.get(num)!.push(pair.layout);
@@ -347,7 +346,6 @@ export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc:
if (!stack && (curTime === undefined || Math.abs(x - (curTime - minTime) * scaling) > pivotAxisWidth)) {
groupNames.push({ type: 'text', text: toLabel(key), x: x, y: stack * 25, height: fontHeight, fontSize, payload: undefined });
}
- // eslint-disable-next-line no-use-before-define
layoutDocsAtTime(keyDocs, key);
});
if (sortedKeys.length && curTime !== undefined && curTime > sortedKeys[sortedKeys.length - 1]) {
@@ -428,6 +426,7 @@ function normalizeResults(
opacity: newPosRaw.opacity,
color: newPosRaw.color,
pair: ele[1].pair,
+ showTags: newPosRaw.showTags,
};
if (newPosRaw.transition) newPos.transition = newPosRaw.transition;
poolData.set(newPos.pair.layout[Id] + (newPos.replica || ''), { transition: 'all 1s', ...newPos });
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 2c94446fb..cce0ff684 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -1,4 +1,4 @@
-@import '../../global/globalCssVariables.module.scss';
+@use '../../global/globalCssVariables.module.scss' as global;
.collectionfreeformview-none {
position: inherit;
@@ -32,9 +32,9 @@
.collectionfreeformview-mask-empty,
.collectionfreeformview-mask {
z-index: 5000;
- width: $INK_MASK_SIZE;
- height: $INK_MASK_SIZE;
- transform: translate($INK_MASK_SIZE_HALF, $INK_MASK_SIZE_HALF);
+ width: global.$INK_MASK_SIZE;
+ height: global.$INK_MASK_SIZE;
+ transform: translate(global.$INK_MASK_SIZE_HALF, global.$INK_MASK_SIZE_HALF);
pointer-events: none;
position: absolute;
background-color: transparent;
@@ -211,11 +211,11 @@
//nested freeform views
// .collectionfreeformview-container {
- // background-image: linear-gradient(to right, $light-color-secondary 1px, transparent 1px),
- // linear-gradient(to bottom, $light-color-secondary 1px, transparent 1px);
+ // background-image: linear-gradient(to right, global.$light-color-secondary 1px, transparent 1px),
+ // linear-gradient(to bottom, global.$light-color-secondary 1px, transparent 1px);
// background-size: 30px 30px;
// }
- border: 0px solid $light-gray;
+ border: 0px solid global.$light-gray;
border-radius: inherit;
box-sizing: border-box;
position: absolute;
@@ -233,7 +233,7 @@
box-sizing: border-box;
width: 98%;
height: 98%;
- border-radius: $border-radius;
+ border-radius: global.$border-radius;
}
//this is an animation for the blinking cursor!
@@ -304,3 +304,65 @@
display: none;
}
}
+
+.collectionFreeformView-aiView {
+ text-align: center;
+ font-weight: bold;
+ width: 100%;
+
+ .collectionfreeformview-aiView-prompt {
+ height: 25px;
+ width: 65%;
+ }
+
+ .collectionFreeFormView-aiView-strength {
+ text-align: center;
+ align-items: center;
+ display: flex;
+ width: 25%;
+ .collectionFreeFormView-aiView-similarity {
+ max-width: 65px;
+ width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ }
+
+ .collectionFreeForView-aiView-send {
+ width: 10%;
+ .button-container {
+ width: 100% !important;
+ justify-content: left !important;
+ }
+ }
+
+ .collectionFreeformView-aiView-options-container,
+ .collectionFreeFormView-aiView-regenerate-container {
+ text-align: start;
+ font-weight: normal;
+ width: 100%;
+ display: flex;
+ .collectionFreeformView-aiView-subtitle {
+ margin: auto;
+ width: 40px;
+ }
+ }
+ .collectionFreeformView-aiView-options,
+ .collectionFreeFormView-aiView-regenerate {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ align-items: center;
+ width: 100%;
+ gap: 10px;
+ .collectionFreeformView-aiView-input {
+ width: 100%;
+ }
+ .collectionFreeFormView-aiView-regenBtn {
+ width: 10%;
+ .button-container {
+ width: 100% !important;
+ }
+ }
+ }
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ff711314b..89aa53c35 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,5 +1,5 @@
import { Bezier } from 'bezier-js';
-import { Colors } from 'browndash-components';
+import { Button, Colors, Type } from '@dash/components';
import { Property } from 'csstype';
import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
@@ -10,7 +10,7 @@ import { DateField } from '../../../../fields/DateField';
import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc';
import { DocData, Height, Width } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
-import { InkData, InkField, InkTool, Segment } from '../../../../fields/InkField';
+import { InkData, InkEraserTool, InkField, InkInkTool, InkTool, Segment } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
import { RichTextField } from '../../../../fields/RichTextField';
import { listSpec } from '../../../../fields/Schema';
@@ -30,19 +30,32 @@ import { CompileScript } from '../../../util/Scripting';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
import { freeformScrollMode, SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
-import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager';
+import { undoable, UndoManager } from '../../../util/UndoManager';
import { Timeline } from '../../animationtimeline/Timeline';
import { ContextMenu } from '../../ContextMenu';
import { InkingStroke } from '../../InkingStroke';
import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
-import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveEraserWidth, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, DocumentView, SetActiveInkColor, SetActiveInkWidth } from '../../nodes/DocumentView';
+import {
+ ActiveInkArrowEnd,
+ ActiveInkArrowStart,
+ ActiveInkDash,
+ ActiveEraserWidth,
+ ActiveInkFillColor,
+ ActiveInkBezierApprox,
+ ActiveInkColor,
+ ActiveInkWidth,
+ ActiveIsInkMask,
+ DocumentView,
+ SetActiveInkColor,
+ SetActiveInkWidth,
+} from '../../nodes/DocumentView';
import { FieldViewProps } from '../../nodes/FieldView';
import { FocusViewOptions } from '../../nodes/FocusViewOptions';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { OpenWhere } from '../../nodes/OpenWhere';
import { PinDocView, PinProps } from '../../PinFuncs';
-import { AnnotationPalette } from '../../smartdraw/AnnotationPalette';
+import { StickerPalette } from '../../smartdraw/StickerPalette';
import { DrawingOptions, SmartDrawHandler } from '../../smartdraw/SmartDrawHandler';
import { StyleProp } from '../../StyleProp';
import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
@@ -54,6 +67,11 @@ import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannable
import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
+import ReactLoading from 'react-loading';
+import { SettingsManager } from '../../../util/SettingsManager';
+import { Slider } from '@mui/material';
+import { AiOutlineSend } from 'react-icons/ai';
+import { DrawingFillHandler } from '../../smartdraw/DrawingFillHandler';
@observer
class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> {
@@ -85,6 +103,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const parent = CollectionFreeFormDocumentView.from(dv)?._props.reactParent;
return parent instanceof CollectionFreeFormView ? parent : undefined;
}
+ /**
+ * The Freeformview 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.
+ */
+ // eslint-disable-next-line no-use-before-define
+ public static DownFfview: CollectionFreeFormView | undefined; // the first DocView that receives a pointerdown event. used by GestureOverlay to determine the doc a gesture should apply to.
private _clusters = new CollectionFreeFormClusters(this);
private _oldWheel: HTMLDivElement | null = null;
@@ -209,7 +232,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
public static gotoKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], duration: number) {
- return DocumentView.SetViewTransition(docs, 'all', duration, timer, undefined, true);
+ return DocumentView.SetViewTransition(docs, 'all', duration, timer, true);
}
changeKeyFrame = (back = false) => {
const currentFrame = Cast(this.Document._currentFrame, 'number', null);
@@ -270,7 +293,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
isAnyChildContentActive = () => this._props.isAnyChildContentActive();
addLiveTextBox = (newDoc: Doc) => {
- Doc.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+ DocumentView.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
this.addDocument(newDoc);
};
selectDocuments = (docs: Doc[]) => {
@@ -321,7 +344,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
focusOnPoint = (options: FocusViewOptions) => {
const { pointFocus, zoomTime, didMove } = options;
if (!this.Document.isGroup && pointFocus && !didMove) {
- const dfltScale = this.isAnnotationOverlay ? 1 : 0.5;
+ const dfltScale = this.isAnnotationOverlay ? 1 : 0.25;
if (this.layoutDoc[this.scaleFieldKey] !== dfltScale) {
this.zoomSmoothlyAboutPt(this.screenToFreeformContentsXf.transformPoint(pointFocus.X, pointFocus.Y), dfltScale, zoomTime);
options.didMove = true;
@@ -464,11 +487,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// do nothing if link is dropped into any freeform view parent of dragged document
const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, x, y, title: 'dropped annotation' });
const added = !!this._props.addDocument?.(source);
- de.complete.linkDocument = DocUtils.MakeLink(linkDragData.linkSourceGetAnchor(), source, { link_relationship: 'annotated by:annotation of' });
- if (de.complete.linkDocument) {
- de.complete.linkDocument.layout_isSvg = true;
- this.addDocument(de.complete.linkDocument);
- }
+ de.complete.linkDocument = DocUtils.MakeLink(linkDragData.linkSourceGetAnchor(), source, { layout_isSvg: true, link_relationship: 'annotated by:annotation of' });
+ de.complete.linkDocument && this.addDocument(de.complete.linkDocument);
e.stopPropagation();
!added && e.preventDefault();
return added;
@@ -487,6 +507,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
onPointerDown = (e: React.PointerEvent): void => {
+ if (!CollectionFreeFormView.DownFfview) CollectionFreeFormView.DownFfview = this;
+
this._downX = this._lastX = e.pageX;
this._downY = this._lastY = e.pageY;
this._downTime = Date.now();
@@ -497,13 +519,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// prettier-ignore
const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
switch (Doc.ActiveTool) {
- case InkTool.Highlighter:
- case InkTool.Write:
- case InkTool.Pen:
+ case InkTool.Ink:
break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
- case InkTool.StrokeEraser:
- case InkTool.SegmentEraser:
- case InkTool.RadiusEraser:
+ case InkTool.Eraser:
this._batch = UndoManager.StartBatch('collectionErase');
this._eraserPts.length = 0;
setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, this.onEraserClick, hit !== -1);
@@ -543,7 +561,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const { points } = ge;
const B = this.screenToFreeformContentsXf.transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
const inkDoc = this.createInkDoc(points, B);
- if (Doc.ActiveTool === InkTool.Write) {
+ if (Doc.ActiveInk === InkInkTool.Highlight) inkDoc[DocData].backgroundColor = 'transparent';
+ if (Doc.ActiveInk === InkInkTool.Write) {
this.unprocessedDocs.push(inkDoc);
CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
}
@@ -606,7 +625,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const currPoint = { X: e.clientX, Y: e.clientY };
this._eraserPts.push([currPoint.X, currPoint.Y]);
this._eraserPts = this._eraserPts.slice(Math.max(0, this._eraserPts.length - 5));
- if (Doc.ActiveTool === InkTool.RadiusEraser) {
+ if (Doc.ActiveEraser === InkEraserTool.Radius) {
const strokeMap: Map<DocumentView, number[]> = this.getRadiusEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint);
strokeMap.forEach((intersects, stroke) => {
if (!this._deleteList.includes(stroke)) {
@@ -614,13 +633,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
SetActiveInkWidth(StrCast(stroke.Document.stroke_width?.toString()) || '1');
SetActiveInkColor(StrCast(stroke.Document.color?.toString()) || 'black');
const segments = this.radiusErase(stroke, intersects.sort());
- segments?.forEach(segment =>
- this.forceStrokeGesture(
- e,
- Gestures.Stroke,
- segment.reduce((data, curve) => [...data, ...curve.points.map(p => stroke.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[])
- )
- );
+ segments?.forEach(segment => {
+ const points = segment.reduce((data, curve) => [...data, ...curve.points.map(p => stroke.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 inkDoc = this.createInkDoc(points, B);
+ ['color', 'fillColor', 'stroke_width', 'stroke_dash', 'stroke_bezier'].forEach(field => {
+ inkDoc[DocData][field] = stroke.dataDoc[field];
+ });
+ this.addDocument(inkDoc);
+ });
}
stroke.layoutDoc.opacity = 0;
stroke.layoutDoc.dontIntersect = true;
@@ -632,7 +654,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
SetActiveInkWidth(StrCast(intersect.inkView.Document.stroke_width?.toString()) || '1');
SetActiveInkColor(StrCast(intersect.inkView.Document.color?.toString()) || 'black');
// create a new curve by appending all curves of the current segment together in order to render a single new stroke.
- if (Doc.ActiveTool !== InkTool.StrokeEraser) {
+ if (Doc.ActiveEraser !== InkEraserTool.Stroke) {
// this._eraserLock++;
const segments = this.segmentErase(intersect.inkView, intersect.t); // intersect.t is where the eraser intersected the ink stroke - want to remove the segment that starts at the intersection just before this t value and goes to the one just after it
const newStrokes = segments?.map(segment => {
@@ -1195,60 +1217,28 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
y: B.y - inkWidth / 2,
_width: B.width + inkWidth,
_height: B.height + inkWidth,
- stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
+ stroke_showLabel: !BoolCast(Doc.UserDoc().activeHideTextLabels)}, // prettier-ignore
inkWidth,
ActiveInkColor(),
ActiveInkBezierApprox(),
- ActiveFillColor(),
- ActiveArrowStart(),
- ActiveArrowEnd(),
- ActiveDash(),
+ ActiveInkFillColor(),
+ ActiveInkArrowStart(),
+ ActiveInkArrowEnd(),
+ ActiveInkDash(),
ActiveIsInkMask()
);
};
@action
- showSmartDraw = (x: number, y: number) => {
- SmartDrawHandler.Instance.CreateDrawingDoc = this.createDrawingDoc;
- SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing;
- SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
- SmartDrawHandler.Instance.displaySmartDrawHandler(x, y);
+ showSmartDraw = (x: number, y: number, regenerate?: boolean) => {
+ const sm = SmartDrawHandler.Instance;
+ sm.RemoveDrawing = this.removeDrawing;
+ sm.AddDrawing = this.addDrawing;
+ (regenerate ? sm.displayRegenerate : sm.displaySmartDrawHandler)(x, y, NumCast(this.layoutDoc[this.scaleFieldKey]));
};
_drawing: Doc[] = [];
_drawingContainer: Doc | undefined = undefined;
- /**
- * Function that creates a drawing--a group of ink strokes--to go with the smart draw function.
- */
- @undoBatch
- createDrawingDoc = (strokeData: [InkData, string, string][], opts: DrawingOptions) => {
- this._drawing = [];
- const xf = this.screenToFreeformContentsXf;
- strokeData.forEach((stroke: [InkData, string, string]) => {
- const bounds = InkField.getBounds(stroke[0]);
- const B = xf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
- const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
- const inkDoc = Docs.Create.InkDocument(
- stroke[0],
- { title: 'stroke',
- x: B.x - inkWidth / 2,
- y: B.y - inkWidth / 2,
- _width: B.width + inkWidth,
- _height: B.height + inkWidth,
- stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
- inkWidth,
- opts.autoColor ? stroke[1] : ActiveInkColor(),
- ActiveInkBezierApprox(),
- stroke[2] === 'none' ? ActiveFillColor() : stroke[2],
- ActiveArrowStart(),
- ActiveArrowEnd(),
- ActiveDash(),
- ActiveIsInkMask()
- );
- this._drawing.push(inkDoc);
- });
- return MarqueeView.getCollection(this._drawing, undefined, true, { left: opts.x, top: opts.y, width: 1, height: 1 });
- };
/**
* Part of regenerating a drawing--deletes the old drawing.
@@ -1269,16 +1259,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
/**
* Adds the created drawing to the freeform canvas and sets the metadata.
*/
- addDrawing = (doc: Doc, opts: DrawingOptions, gptRes: string) => {
+ addDrawing = (doc: Doc, opts: DrawingOptions, gptRes: string, x?: number, y?: number) => {
const docData = doc[DocData];
- docData.title = opts.text.match(/^(.*?)~~~.*$/)?.[1] || opts.text;
- docData.width = opts.size;
- docData.drawingInput = opts.text;
- docData.drawingComplexity = opts.complexity;
- docData.drawingColored = opts.autoColor;
- docData.drawingSize = opts.size;
- docData.drawingData = gptRes;
+ docData.title = opts.text;
+ docData._width = opts.size;
+ docData.ai_drawing_input = opts.text;
+ docData.ai_drawing_complexity = opts.complexity;
+ docData.ai_drawing_colored = opts.autoColor;
+ docData.ai_drawing_size = opts.size;
+ docData.ai_drawing_data = gptRes;
+ docData.ai = 'gpt';
this._drawingContainer = doc;
+ if (x !== undefined && y !== undefined) {
+ [doc.x, doc.y] = this.screenToFreeformContentsXf.transformPoint(x, y);
+ }
this.addDocument(doc);
this._batch?.end();
};
@@ -1307,6 +1301,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.Document[this.scaleFieldKey] = Math.abs(safeScale);
this.setPan(-localTransform.TranslateX / safeScale, (this._props.originTopLeft ? undefined : NumCast(this.Document.layout_scrollTop) * safeScale) || -localTransform.TranslateY / safeScale, undefined, allowScroll);
}
+ SmartDrawHandler.Instance.hideSmartDrawHandler();
};
@action
@@ -1501,8 +1496,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
newDoc[DocData][Doc.LayoutFieldKey(newDoc, fieldProps.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style
newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10);
newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0);
- Doc.SetSelectOnLoad(newDoc);
- FormattedTextBox.DontSelectInitialText = true;
+ DocumentView.SetSelectOnLoad(newDoc);
return this.addDocument?.(newDoc);
}, 'copied text note');
@@ -1634,6 +1628,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
width: _width,
height: _height,
transition: StrCast(childDocLayout.dataTransition),
+ showTags: BoolCast(childDocLayout.showTags) || BoolCast(this.Document.showChildTags) || BoolCast(this.Document._layout_showTags),
pointerEvents: Cast(childDoc.pointerEvents, 'string', null),
pair,
replica: '',
@@ -1847,14 +1842,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
updateIcon = (usePanelDimensions?: boolean) => {
- const contentDiv = this.DocumentView?.().ContentDiv;
+ const contentDiv = this._mainCont;
return !contentDiv
? new Promise<void>(res => res())
: UpdateIcon(
this.layoutDoc[Id] + '_icon_' + new Date().getTime(),
contentDiv,
- usePanelDimensions ? this._props.PanelWidth() : NumCast(this.layoutDoc._width),
- usePanelDimensions ? this._props.PanelHeight() : NumCast(this.layoutDoc._height),
+ usePanelDimensions || true ? this._props.PanelWidth() : NumCast(this.layoutDoc._width),
+ usePanelDimensions || true ? this._props.PanelHeight() : NumCast(this.layoutDoc._height),
this._props.PanelWidth(),
this._props.PanelHeight(),
0,
@@ -1974,20 +1969,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}),
icon: 'eye',
});
+ this.layoutDoc.drawingData != undefined &&
+ optionItems.push({
+ description: 'Regenerate AI Drawing',
+ event: action(() => {
+ 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',
+ });
optionItems.push({
- description: 'Show Drawing Editor',
- event: action(() => {
- 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',
- });
- optionItems.push({
- description: this.Document.savedAsAnno ? 'Saved as Annotation!' : 'Save to Annotation Palette',
- event: action(undoable(async () => await AnnotationPalette.addToPalette(this.Document), 'save to palette')),
- icon: this.Document.savedAsAnno ? 'clipboard-check' : 'file-arrow-down',
+ description: this.Document.savedAsSticker ? 'Sticker Saved!' : 'Save to Stickers',
+ event: action(undoable(async () => await StickerPalette.addToPalette(this.Document), 'save to palette')),
+ icon: this.Document.savedAsSticker ? 'clipboard-check' : 'file-arrow-down',
});
this._props.renderDepth &&
optionItems.push({
@@ -2166,6 +2161,102 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
</div>
);
}
+
+ @observable private _regenInput = '';
+ @observable private _drawingFillInput = '';
+ @observable private _regenLoading = false;
+ @observable private _drawingFillLoading = false;
+ @observable private _fireflyRefStrength = 50;
+
+ componentAIView = () => {
+ return (
+ <div className="collectionfreeformview-aiView" onPointerDown={e => e.stopPropagation()}>
+ <div className="collectionfreeformview-aiView-options-container">
+ <span className="collectionfreeformview-aiView-subtitle">Firefly:</span>
+ <div className="collectionfreeformview-aiView-options">
+ <input
+ className="collectionfreeformview-aiView-prompt"
+ placeholder={this._drawingFillInput || StrCast(this.Document.title) || 'Describe image'}
+ type="text"
+ value={this._drawingFillInput}
+ onChange={action(e => {
+ this._drawingFillInput = e.target.value;
+ })}
+ />
+ <div className="collectionFreeFormView-aiView-strength">
+ <span className="collectionFreeFormView-aiView-similarity">Similarity</span>
+ <Slider
+ className="collectionfreeformview-aiView-slider"
+ sx={{
+ '& .MuiSlider-track': { color: SettingsManager.userVariantColor },
+ '& .MuiSlider-rail': { color: SettingsManager.userBackgroundColor },
+ '& .MuiSlider-thumb': { color: SettingsManager.userVariantColor, '&.Mui-focusVisible, &:hover, &.Mui-active': { boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}10` } },
+ }}
+ min={1}
+ max={100}
+ step={1}
+ size="small"
+ value={this._fireflyRefStrength}
+ onChange={action((e, val) => (this._fireflyRefStrength = val as number))}
+ valueLabelDisplay="auto"
+ />
+ </div>
+ <div className="collectionFreeFormView-aiView-send">
+ <Button
+ text="Send"
+ type={Type.SEC}
+ icon={this._drawingFillLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />}
+ iconPlacement="right"
+ onClick={undoable(
+ action(() => {
+ this._drawingFillLoading = true;
+ DrawingFillHandler.drawingToImage(this.props.Document, this._fireflyRefStrength, this._drawingFillInput || StrCast(this.Document.title))?.then(
+ action(() => {
+ this._drawingFillLoading = false;
+ })
+ );
+ }),
+ 'create image'
+ )}
+ />
+ </div>
+ </div>
+ </div>
+ <div className="collectionfreeformview-aiView-regenerate-container">
+ <span className="collectionfreeformview-aiView-subtitle">Regenerate</span>
+ <div className="collectionfreeformview-aiView-regenerate">
+ <input
+ className="collectionfreeformview-aiView-input"
+ aria-label="Edit instructions input"
+ type="text"
+ value={this._regenInput}
+ onChange={action(e => {
+ this._regenInput = e.target.value;
+ })}
+ placeholder="..under development.."
+ />
+ <div className="collectionFreeFormView-aiView-regenBtn">
+ <Button
+ text="Regenerate"
+ type={Type.SEC}
+ icon={this._regenLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />}
+ iconPlacement="right"
+ // onClick={action(async () => {
+ // this._regenLoading = true;
+ // SmartDrawHandler.Instance.CreateDrawingDoc = this.createDrawingDoc;
+ // SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
+ // SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing;
+ // await SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput, true);
+ // this._regenLoading = false;
+ // })}
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ };
+
render() {
TraceMobx();
return (
@@ -2195,7 +2286,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
width: `${100 / this.nativeDimScaling}%`,
height: this._props.getScrollHeight?.() ?? `${100 / this.nativeDimScaling}%`,
}}>
- {Doc.ActiveTool === InkTool.RadiusEraser && this._showEraserCircle && (
+ {Doc.ActiveTool === InkTool.Eraser && Doc.ActiveEraser === InkEraserTool.Radius && this._showEraserCircle && (
<div
onPointerMove={this.onCursorMove}
style={{
diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
index 6d51ecac6..b9f8b13a7 100644
--- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { IconButton, Size } from 'browndash-components';
+import { IconButton, Size } from '@dash/components';
import * as faceapi from 'face-api.js';
import { FaceMatcher } from 'face-api.js';
import 'ldrs/ring';
diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
index 583f2e656..a3d9641da 100644
--- a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Colors, IconButton } from 'browndash-components';
+import { Colors, IconButton } from '@dash/components';
import similarity from 'compute-cosine-similarity';
import { ring } from 'ldrs';
import 'ldrs/ring';
diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelHandler.tsx b/src/client/views/collections/collectionFreeForm/ImageLabelHandler.tsx
index 73befb205..1a44e094d 100644
--- a/src/client/views/collections/collectionFreeForm/ImageLabelHandler.tsx
+++ b/src/client/views/collections/collectionFreeForm/ImageLabelHandler.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { IconButton } from 'browndash-components';
+import { IconButton } from '@dash/components';
import { action, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import React from 'react';
@@ -9,7 +9,8 @@ import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
import './ImageLabelHandler.scss';
@observer
-export class ImageLabelHandler extends ObservableReactComponent<{}> {
+export class ImageLabelHandler extends ObservableReactComponent<object> {
+ // eslint-disable-next-line no-use-before-define
static Instance: ImageLabelHandler;
@observable _display: boolean = false;
@@ -19,11 +20,10 @@ export class ImageLabelHandler extends ObservableReactComponent<{}> {
@observable _currentLabel: string = '';
@observable _labelGroups: string[] = [];
- constructor(props: any) {
+ constructor(props: object) {
super(props);
makeObservable(this);
ImageLabelHandler.Instance = this;
- console.log('Instantiated label handler!');
}
@action
@@ -41,8 +41,8 @@ export class ImageLabelHandler extends ObservableReactComponent<{}> {
};
@action
- addLabel = (label: string) => {
- label = label.toUpperCase().trim();
+ addLabel = (labelIn: string) => {
+ const label = labelIn.toUpperCase().trim();
if (label.length > 0) {
if (!this._labelGroups.includes(label)) {
this._labelGroups = [...this._labelGroups, label];
@@ -96,10 +96,10 @@ export class ImageLabelHandler extends ObservableReactComponent<{}> {
<div>
{this._labelGroups.map(group => {
return (
- <div>
+ <div key={group}>
<p>{group}</p>
<IconButton
- tooltip={'Remove Label'}
+ tooltip="Remove Label"
onPointerDown={() => {
this.removeLabel(group);
}}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index de65b240f..abd828945 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { IconButton } from 'browndash-components';
+import { IconButton } from '@dash/components';
import { computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index c865c681d..00d7ea451 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -173,7 +173,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
const slide = DocUtils.copyDragFactory(DocCast(Doc.UserDoc().emptySlide))!;
slide.x = x;
slide.y = y;
- Doc.SetSelectOnLoad(slide);
+ DocumentView.SetSelectOnLoad(slide);
TreeView._editTitleOnLoad = { id: slide[Id], parent: undefined };
this._props.addDocument?.(slide);
e.stopPropagation();
@@ -461,6 +461,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
const newColDim = 900;
for (const label of labelGroups) {
const newCollection = MarqueeView.getCollection([], undefined, false, this.Bounds);
+ newCollection[DocData].title = label + ' Collection';
newCollection._x = this.Bounds.left + x_offset;
newCollection._y = this.Bounds.top + y_offset;
newCollection._width = newColDim;
@@ -586,7 +587,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
/**
* When this is called, returns the list of documents that have been selected by the marquee box.
*/
- marqueeSelect(selectBackgrounds: boolean = false, docType: DocumentType | undefined = undefined) {
+ marqueeSelect = (selectBackgrounds: boolean = false, docType: DocumentType | undefined = undefined) => {
const selection: Doc[] = [];
const selectFunc = (doc: Doc) => {
const layoutDoc = Doc.Layout(doc);
@@ -619,7 +620,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
.filter(doc => doc.z !== undefined)
.map(selectFunc);
return selection;
- }
+ };
@computed get marqueeDiv() {
const cpt = this._lassoFreehand || !this._visible ? [0, 0] : [this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY];
@@ -690,7 +691,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
}}
style={{
overflow: StrCast(this._props.Document._overflow),
- cursor: [InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool) || this._visible ? 'crosshair' : 'pointer',
+ cursor: Doc.ActiveTool === InkTool.Ink || this._visible ? 'crosshair' : 'pointer',
}}
onDragOver={e => e.preventDefault()}
onScroll={e => {