diff options
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
10 files changed, 210 insertions, 43 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/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 79aad0ef2..272c13546 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -102,7 +102,6 @@ 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, []); } @@ -272,7 +271,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, []); } @@ -347,7 +345,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]) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index 2c94446fb..46bd37f6d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -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 f2a5780c5..796949378 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'; @@ -55,7 +55,7 @@ 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'; @@ -67,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[] }> { @@ -98,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; @@ -477,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; @@ -500,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(); @@ -1225,7 +1234,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection SmartDrawHandler.Instance.CreateDrawingDoc = this.createDrawingDoc; SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing; SmartDrawHandler.Instance.AddDrawing = this.addDrawing; - SmartDrawHandler.Instance.displaySmartDrawHandler(x, y); + SmartDrawHandler.Instance.displaySmartDrawHandler(x, y, NumCast(this.layoutDoc[this.scaleFieldKey])); }; _drawing: Doc[] = []; @@ -1285,12 +1294,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection addDrawing = (doc: Doc, opts: DrawingOptions, gptRes: string) => { 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._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; this.addDocument(doc); this._batch?.end(); @@ -1320,6 +1330,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 @@ -1860,14 +1871,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, @@ -1987,20 +1998,21 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }), icon: 'eye', }); + this.layoutDoc.drawingData != undefined && + optionItems.push({ + description: 'Regenerate AI Drawing', + 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: '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({ @@ -2179,6 +2191,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 ( 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..f050b9846 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'; 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 ddc50871d..5524fedb3 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -586,7 +586,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 +619,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]; |
