From 19adcca1a89405a47da57fee64fe5fde4720c16a Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 5 May 2025 14:05:32 -0400 Subject: got rid of dash _xPadding/_yPadding in favor of just using _xMargin/_yMargin. cleaned up linearView field names --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 2364fd74a..05958e6b9 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -256,8 +256,8 @@ export class CollectionFreeFormView extends CollectionSubView { const { x, y, r, b } = aggregateBounds( this._layoutElements.filter(e => e.bounds?.width && !e.bounds.z).map(e => e.bounds!), - NumCast(this.layoutDoc._xPadding, NumCast(this.layoutDoc._xMargin, this._props.xPadding ?? 0)), - NumCast(this.layoutDoc._yPadding, NumCast(this.layoutDoc._yMargin, this._props.yPadding ?? 0)) + NumCast(this.layoutDoc._xMargin, this._props.xMargin ?? 0), + NumCast(this.layoutDoc._yMargin, this._props.yMargin ?? 0) ); const [width, height] = [r - x, b - y]; return { @@ -1775,7 +1775,7 @@ export class CollectionFreeFormView extends CollectionSubView { if (this.Document.isGroup && this.childDocs.length === this.childDocList?.length) { const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: NumCast(cd._width), height: NumCast(cd._height) })); - return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding)); + return aggregateBounds(clist, NumCast(this.layoutDoc._xMargin), NumCast(this.layoutDoc._yMargin)); } return undefined; }, -- cgit v1.2.3-70-g09d2 From 4b8c4c9277cca69ea3341ca6ddf4c7f5befc78a0 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 7 May 2025 15:46:06 -0400 Subject: cleaned up ai metadata on generated drawings. --- src/client/documents/Documents.ts | 5 +- src/client/util/DocumentManager.ts | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 54 ++----------------- src/client/views/nodes/ImageBox.tsx | 6 +-- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 4 +- src/client/views/smartdraw/DrawingFillHandler.tsx | 6 +-- src/client/views/smartdraw/SmartDrawHandler.scss | 1 + src/client/views/smartdraw/SmartDrawHandler.tsx | 63 +++++++++++----------- src/client/views/smartdraw/StickerPalette.tsx | 12 ++--- 9 files changed, 57 insertions(+), 96 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index bbe872a91..9b6acef7b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -519,8 +519,9 @@ export class DocumentOptions { card_sort_isDesc?: BOOLt = new BoolInfo('whether the cards are sorted ascending or descending'); ai?: string; // to mark items as ai generated - ai_firefly_seed?: number; - ai_firefly_prompt?: string; + ai_prompt_seed?: NUMt = new NumInfo('seed to GAI engine to make results deterministic'); + ai_prompt?: STRt = new StrInfo('input prompt to GAI engine'); + ai_generatedDocs?: List; // list of documents generated by GAI engine /** * JSON‐stringified slot configuration for ScrapbookBox diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 3bae2881e..fc8f22cf3 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -349,7 +349,7 @@ export class DocumentManager { // bcz: should this delay be an options parameter? setTimeout(() => { Doc.linkFollowHighlight(viewSpec ? [docView.Document, viewSpec] : docView.Document, undefined, options.effect); - const zoomableText = StrCast(targetDoc.text_html, StrCast(targetDoc.ai_firefly_prompt)); + const zoomableText = StrCast(targetDoc.text_html, StrCast(targetDoc.ai_prompt)); if (options.zoomTextSelections && Doc.IsUnhighlightTimerSet() && contextView && zoomableText) { // if the docView is a text anchor, the contextView is the PDF/Web/Text doc contextView.setTextHtmlOverlay(zoomableText, options.effect); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 05958e6b9..bb3c59eae 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1259,15 +1259,8 @@ export class CollectionFreeFormView extends CollectionSubView { - doc.$title = opts.text; - doc.$width = opts.size; - doc.$ai_drawing_input = opts.text; - doc.$ai_drawing_complexity = opts.complexity; - doc.$ai_drawing_colored = opts.autoColor; - doc.$ai_drawing_size = opts.size; - doc.$ai_drawing_data = gptRes; - doc.$ai = 'gpt'; + addDrawing = (doc: Doc, opts: DrawingOptions, x?: number, y?: number) => { + doc.$ai_prompt = opts.text; this._drawingContainer = doc; if (x !== undefined && y !== undefined) { [doc.x, doc.y] = this.screenToFreeformContentsXf.transformPoint(x, y); @@ -1977,7 +1970,7 @@ export class CollectionFreeFormView extends CollectionSubView { SmartDrawHandler.Instance.AddDrawing = this.addDrawing; SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing; - !SmartDrawHandler.Instance.ShowRegenerate ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10) : SmartDrawHandler.Instance.hideRegenerate(); + !SmartDrawHandler.Instance.ShowRegenerate ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10, NumCast(this.layoutDoc[this.scaleFieldKey])) : SmartDrawHandler.Instance.hideRegenerate(); }), icon: 'pen-to-square', }); @@ -2182,9 +2175,7 @@ export class CollectionFreeFormView extends CollectionSubView { - this._drawingFillInput = e.target.value; - })} + onChange={action(e => (this._drawingFillInput = e.target.value))} />
Similarity @@ -2213,11 +2204,7 @@ export class CollectionFreeFormView extends CollectionSubView { this._drawingFillLoading = true; - DrawingFillHandler.drawingToImage(this.props.Document, this._fireflyRefStrength, this._drawingFillInput || StrCast(this.Document.title))?.then( - action(() => { - this._drawingFillLoading = false; - }) - ); + DrawingFillHandler.drawingToImage(this.props.Document, this._fireflyRefStrength, this._drawingFillInput || StrCast(this.Document.title))?.then(action(() => (this._drawingFillLoading = false))); }), 'create image' )} @@ -2225,37 +2212,6 @@ export class CollectionFreeFormView extends CollectionSubView
-
- Regenerate -
- { - this._regenInput = e.target.value; - })} - placeholder="..under development.." - /> -
-
-
-
); }; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index bf6915570..b648f2adb 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -225,7 +225,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { const drag = de.complete.docDragData.draggedDocuments.lastElement(); const dragField = drag[Doc.LayoutDataKey(drag)]; const descText = RTFCast(dragField)?.Text || StrCast(dragField) || RTFCast(drag.text)?.Text || StrCast(drag.text) || StrCast(this.Document.title); - const oldPrompt = StrCast(this.Document.ai_firefly_prompt, StrCast(this.Document.title)); + const oldPrompt = StrCast(this.Document.ai_prompt, StrCast(this.Document.title)); const newPrompt = (text: string) => (oldPrompt ? `${oldPrompt} ~~~ ${text}` : text); DrawingFillHandler.drawingToImage(this.Document, 90, newPrompt(descText), drag)?.then(action(() => (this._regenerateLoading = false))); added = false; @@ -735,9 +735,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent() {
DocCast(this.Document.ai_firefly_generatedDocs) && DocumentView.showDocument(DocCast(this.Document.ai_firefly_generatedDocs)!, { openLocation: OpenWhere.addRight })} + onClick={() => DocCast(this.Document.ai_generatedDocs) && DocumentView.showDocument(DocCast(this.Document.ai_generatedDocs)!, { openLocation: OpenWhere.addRight })} style={{ - display: (this._props.isContentActive() && (SnappingManager.CanEmbed || this.Document.ai_firefly_generatedDocs)) || this._regenerateLoading ? 'block' : 'none', + display: (this._props.isContentActive() && (SnappingManager.CanEmbed || this.Document.ai_generatedDocs)) || this._regenerateLoading ? 'block' : 'none', transform: `scale(${this.uiBtnScaling})`, width: this._sideBtnWidth, height: this._sideBtnWidth, diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index 9fbae5c90..568e48edf 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -211,7 +211,7 @@ export class GPTPopup extends ObservableReactComponent { const selView = DocumentView.Selected().lastElement(); const selDoc = selView?.Document; if (selDoc && (selView._props.renderDepth > 1 || selDoc[Doc.LayoutDataKey(selDoc)] instanceof ImageField)) { - const oldPrompt = StrCast(selDoc.ai_firefly_prompt, StrCast(selDoc.title)); + const oldPrompt = StrCast(selDoc.ai_prompt, StrCast(selDoc.title)); const newPrompt = oldPrompt ? `${oldPrompt} ~~~ ${imgDesc}` : imgDesc; return DrawingFillHandler.drawingToImage(selDoc, 100, newPrompt, selDoc) .then(action(() => (this._userPrompt = ''))) @@ -767,7 +767,7 @@ export class GPTPopup extends ObservableReactComponent { //prettier-ignore switch (this._mode) { case GPTPopupMode.USER_PROMPT: return this.promptBox("ASK", this._userPrompt, this.setUserPrompt, 'Ask GPT to sort, tag, define, or filter your documents for you!'); - case GPTPopupMode.FIREFLY: return this.promptBox("CREATE", this._userPrompt, this.setUserPrompt, StrCast(DocumentView.Selected().lastElement()?.Document.ai_firefly_prompt, 'Ask Firefly to generate images')); + case GPTPopupMode.FIREFLY: return this.promptBox("CREATE", this._userPrompt, this.setUserPrompt, StrCast(DocumentView.Selected().lastElement()?.Document.ai_prompt, 'Ask Firefly to generate images')); case GPTPopupMode.QUIZ_RESPONSE: return this.promptBox("QUIZ", this._quizAnswer, this.setQuizAnswer, 'Describe/answer the selected document!'); case GPTPopupMode.GPT_MENU: return this.menuBox(); case GPTPopupMode.SUMMARY: return this.summaryBox(); diff --git a/src/client/views/smartdraw/DrawingFillHandler.tsx b/src/client/views/smartdraw/DrawingFillHandler.tsx index 2c69284db..f773957e7 100644 --- a/src/client/views/smartdraw/DrawingFillHandler.tsx +++ b/src/client/views/smartdraw/DrawingFillHandler.tsx @@ -51,19 +51,19 @@ export class DrawingFillHandler { if (error.includes('Dropbox') && confirm('Create image failed. Try authorizing DropBox?\r\n' + error.replace(/^[^"]*/, ''))) { return DrawingFillHandler.authorizeDropbox(); } - const genratedDocs = DocCast(drawing.ai_firefly_generatedDocs) ?? Docs.Create.MasonryDocument([], { title: StrCast(drawing.title) + ' AI Images', _width: 400, _height: 400 }); - drawing.$ai_firefly_generatedDocs = genratedDocs; + const genratedDocs = DocCast(drawing.ai_generatedDocs) ?? Docs.Create.MasonryDocument([], { title: StrCast(drawing.title) + ' AI Images', _width: 400, _height: 400 }); + drawing.$ai_generatedDocs = genratedDocs; (res as Upload.ImageInformation[]).map(info => Doc.AddDocToList( genratedDocs, undefined, Docs.Create.ImageDocument(info.accessPaths.agnostic.client, { ai: 'firefly', + ai_prompt: newPrompt, tags: new List(['@ai']), title: newPrompt, _data_usePath: 'alternate:hover', data_alternates: new List([drawing]), - ai_firefly_prompt: newPrompt, _width: 500, data_nativeWidth: info.nativeWidth, data_nativeHeight: info.nativeHeight, diff --git a/src/client/views/smartdraw/SmartDrawHandler.scss b/src/client/views/smartdraw/SmartDrawHandler.scss index cca7d77c7..e80f1122b 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.scss +++ b/src/client/views/smartdraw/SmartDrawHandler.scss @@ -73,5 +73,6 @@ .edit-box { display: flex; flex-direction: row; + color: black; } } diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx index a6e221f3e..3976ec39e 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.tsx +++ b/src/client/views/smartdraw/SmartDrawHandler.tsx @@ -61,7 +61,6 @@ export class SmartDrawHandler extends ObservableReactComponent { static Instance: SmartDrawHandler; private _lastInput: DrawingOptions = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 }; - private _lastResponse: string = ''; private _selectedDocs: Doc[] = []; @observable private _display: boolean = false; @@ -97,7 +96,7 @@ export class SmartDrawHandler extends ObservableReactComponent { CollectionFreeForm, FormattedTextBox, StickerPalette) to define how a drawing document should be added or removed in their respective locations (to the freeform canvas, to the sticker palette's preview, etc.) */ - public AddDrawing: (doc: Doc, opts: DrawingOptions, gptRes: string, x?: number, y?: number) => void = unimplementedFunction; + public AddDrawing: (doc: Doc, opts: DrawingOptions, x?: number, y?: number) => void = unimplementedFunction; public RemoveDrawing: (useLastContainer: boolean, doc?: Doc) => void = unimplementedFunction; /** * This creates the ink document that represents a drawing, so it goes through the strokes that make up the drawing, @@ -105,7 +104,7 @@ export class SmartDrawHandler extends ObservableReactComponent { * classes to customize the way the drawing docs get created. For example, the freeform canvas has a different way of * defining document bounds, so CreateDrawingDoc is redefined when that class calls gpt draw functions. */ - public static CreateDrawingDoc: (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => Doc | undefined = (strokeList: [InkData, string, string][], opts: DrawingOptions) => { + public static 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]); @@ -130,7 +129,14 @@ export class SmartDrawHandler extends ObservableReactComponent { drawing.push(inkDoc); }); - return MarqueeView.getCollection(drawing, undefined, true, { left: 1, top: 1, width: 1, height: 1 }); + const drawn = MarqueeView.getCollection(drawing, undefined, true, { left: 1, top: 1, width: 1, height: 1 }); + + drawn.$ai_drawing = true; + drawn.$ai_drawing_complexity = opts.complexity; + drawn.$ai_drawing_colored = opts.autoColor; + drawn.$ai_drawing_size = opts.size; + drawn.$ai_drawing_data = gptRes; + return drawn; }; @action @@ -146,15 +152,16 @@ export class SmartDrawHandler extends ObservableReactComponent { * the regenerate popup show by user command. */ @action - displayRegenerate = (x: number, y: number) => { + displayRegenerate = (x: number, y: number, scale: number) => { this._selectedDocs = [DocumentView.SelectedDocs()?.lastElement()]; [this._pageX, this._pageY] = [x, y]; + this._scale = scale; this._display = false; this.ShowRegenerate = true; this._showEditBox = false; const docData = this._selectedDocs[0]; - this._lastResponse = StrCast(docData.$drawingData); - this._lastInput = { text: StrCast(docData.$ai_drawing_input), complexity: NumCast(docData.$ai_drawing_complexity), size: NumCast(docData.$ai_drawing_size), autoColor: BoolCast(docData.$ai_drawing_colored), x: this._pageX, y: this._pageY }; + this._regenInput = StrCast(docData.$ai_prompt, StrCast(docData.title)); + this._lastInput = { text: StrCast(docData.$ai_prompt), complexity: NumCast(docData.$ai_drawing_complexity), size: NumCast(docData.$ai_drawing_size), autoColor: BoolCast(docData.$ai_drawing_colored), x: this._pageX, y: this._pageY }; }; /** @@ -168,9 +175,6 @@ export class SmartDrawHandler extends ObservableReactComponent { this._isLoading = false; this._showOptions = false; this._userInput = ''; - this._complexity = 5; - this._size = 350; - this._autoColor = true; Doc.ActiveTool = InkTool.None; } }; @@ -184,7 +188,6 @@ export class SmartDrawHandler extends ObservableReactComponent { this.ShowRegenerate = false; this._isLoading = false; this._regenInput = ''; - this._lastInput = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 }; } }; @@ -208,7 +211,9 @@ export class SmartDrawHandler extends ObservableReactComponent { this._isLoading = true; this._canInteract = false; if (this.ShowRegenerate) { - await this.regenerate(this._selectedDocs, undefined, undefined, this._regenInput).then(action(() => (this._showEditBox = false))); + this._lastInput.x = X; + this._lastInput.y = Y; + await this.regenerate(this._selectedDocs).then(action(() => (this._showEditBox = false))); } else { this._showOptions = false; try { @@ -234,13 +239,15 @@ export class SmartDrawHandler extends ObservableReactComponent { */ drawWithGPT = async (screenPt: { X: number; Y: number }, input: string, complexity: number, size: number, autoColor: boolean) => { if (input) { - this._lastInput = { text: input, complexity: complexity, size: size, autoColor: autoColor, x: screenPt.X, y: screenPt.Y }; + this._lastInput = { text: input, complexity, size, autoColor, x: screenPt.X, y: screenPt.Y }; const res = await gptAPICall(`"${input}", "${complexity}", "${size}"`, GPTCallType.DRAW, undefined, true); if (res) { const strokeData = await this.parseSvg(res, { X: 0, Y: 0 }, false, autoColor); const drawingDoc = strokeData && SmartDrawHandler.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes); - drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res, screenPt.X, screenPt.Y); - drawingDoc && this._selectedDocs.push(drawingDoc); + if (drawingDoc) { + this.AddDrawing(drawingDoc, this._lastInput, screenPt.X, screenPt.Y); + this._selectedDocs.push(drawingDoc); + } return strokeData; } else { console.error('GPT call failed'); @@ -255,7 +262,7 @@ export class SmartDrawHandler extends ObservableReactComponent { createImageWithFirefly = (input: string, seed?: number): Promise => { this._lastInput.text = input; return SmartDrawHandler.CreateWithFirefly(input, this._imgDims, seed).then(doc => { - doc instanceof Doc && this.AddDrawing(doc, this._lastInput, input, this._pageX, this._pageY); + doc instanceof Doc && this.AddDrawing(doc, this._lastInput, this._pageX, this._pageY); return doc; }); }; /** @@ -301,8 +308,8 @@ export class SmartDrawHandler extends ObservableReactComponent { _width: Math.min(400, dims.width), _height: (Math.min(400, dims.width) * dims.height) / dims.width, ai: 'firefly', - ai_firefly_seed: +(newseed ?? 0), - ai_firefly_prompt: input, + ai_prompt_seed: +(newseed ?? 0), + ai_prompt: input, }); }) .catch(e => { @@ -316,33 +323,30 @@ export class SmartDrawHandler extends ObservableReactComponent { * @param doc the drawing Docs to regenerate */ @action - regenerate = (drawingDocs: Doc[], lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string, changeInPlace?: boolean) => { - if (lastInput) this._lastInput = lastInput; - if (lastResponse) this._lastResponse = lastResponse; + regenerate = (drawingDocs: Doc[], regenInput?: string, changeInPlace?: boolean) => { if (regenInput) this._regenInput = regenInput; return Promise.all( drawingDocs.map(async doc => { switch (doc.type) { case DocumentType.IMG: { const func = changeInPlace ? this.recreateImageWithFirefly : this.createImageWithFirefly; - const newPrompt = doc.ai_firefly_prompt ? `${doc.ai_firefly_prompt} ~~~ ${this._regenInput}` : this._regenInput; - return this._regenInput ? func(newPrompt, NumCast(doc?.ai_firefly_seed)) : func(this._lastInput.text || StrCast(doc.ai_firefly_prompt)); + const newPrompt = doc.ai_prompt && doc.ai_prompt !== this._regenInput ? `${doc.ai_prompt} ~~~ ${this._regenInput}` : this._regenInput; + return this._regenInput ? func(newPrompt, NumCast(doc?.ai_prompt_seed)) : func(this._lastInput.text || StrCast(doc.ai_prompt)); } case DocumentType.COL: { try { const res = await (async () => { if (this._regenInput) { - const prompt = `This is your previously generated svg code: ${this._lastResponse} for the user input "${this._lastInput.text}". Please regenerate it with the provided specifications.`; + const prompt = `This is your previously generated svg code: ${doc.$ai_drawing_data} for the user input "${doc.ai_prompt}". Please regenerate it with the provided specifications.`; this._lastInput.text = `${this._lastInput.text} ~~~ ${this._regenInput}`; return gptAPICall(`"${this._regenInput}"`, GPTCallType.DRAW, prompt, true); } - return gptAPICall(`"${this._lastInput.text}", "${this._lastInput.complexity}", "${this._lastInput.size}"`, GPTCallType.DRAW, undefined, true); + return gptAPICall(`"${doc.$ai_prompt}", "${doc.$ai_drawing_complexity}", "${doc.$ai_drawing_size}"`, GPTCallType.DRAW, undefined, true); })(); if (res) { - const strokeData = await this.parseSvg(res, { X: this._lastInput.x ?? 0, Y: this._lastInput.y ?? 0 }, true, lastInput?.autoColor || this._autoColor); - this.RemoveDrawing !== unimplementedFunction && this.RemoveDrawing(true, doc); + const strokeData = await this.parseSvg(res, { X: 0, Y: 0 }, true, this._autoColor); const drawingDoc = strokeData && SmartDrawHandler.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes); - drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res); + drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, this._lastInput.x, this._lastInput.y); } else { console.error('GPT call failed'); } @@ -363,7 +367,6 @@ export class SmartDrawHandler extends ObservableReactComponent { const svg = res.match(/]*>([\s\S]*?)<\/svg>/g); if (svg) { - this._lastResponse = svg[0]; const svgObject = await parse(svg[0]); console.log(res, svgObject); const svgStrokes: INode[] = svgObject.children; @@ -664,7 +667,7 @@ export class SmartDrawHandler extends ObservableReactComponent {
: } + icon={this._isLoading ? : } color={SettingsManager.userColor} onClick={() => this.handleSendClick(this._pageX, this._pageY)} /> diff --git a/src/client/views/smartdraw/StickerPalette.tsx b/src/client/views/smartdraw/StickerPalette.tsx index 0e234e966..6ef3d26ad 100644 --- a/src/client/views/smartdraw/StickerPalette.tsx +++ b/src/client/views/smartdraw/StickerPalette.tsx @@ -9,7 +9,7 @@ import ReactLoading from 'react-loading'; import { returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils'; import { emptyFunction, numberRange } from '../../../Utils'; import { Doc, DocListCast, returnEmptyDoclist } from '../../../fields/Doc'; -import { ImageCast, NumCast } from '../../../fields/Types'; +import { ImageCast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; @@ -147,9 +147,9 @@ export class StickerPalette extends ObservableReactComponent { + numberRange(3).map(() => { return this._showRegenerate - ? SmartDrawHandler.Instance.regenerate(prevDrawings, this._opts, this._gptRes[i], this._userInput) + ? SmartDrawHandler.Instance.regenerate(prevDrawings, this._userInput) : SmartDrawHandler.Instance.drawWithGPT({ X: 0, Y: 0 }, this._userInput, this._opts.complexity || 0, this._opts.size || 0, !!this._opts.autoColor); }) ).then(() => { @@ -161,8 +161,8 @@ export class StickerPalette extends ObservableReactComponent { - this._gptRes.push(gptRes); + addDrawing = (drawing: Doc) => { + this._gptRes.push(StrCast(drawing.$ai_drawing_data)); drawing.$freeform_fitContentsToBox = true; Doc.AddDocToList(this._props.Doc, 'data', drawing); }; @@ -176,7 +176,7 @@ export class StickerPalette extends ObservableReactComponent Date: Tue, 13 May 2025 13:37:54 -0400 Subject: fixed setting up link anchors for video to be added to Doc. fixed imageTemplate to work with templates in tabs. fixed link following from link menu. cleaned up getView with sidebars to open them when needed properly.. --- src/client/documents/DocUtils.ts | 2 +- src/client/util/DocumentManager.ts | 2 +- src/client/views/MarqueeAnnotator.tsx | 3 +- src/client/views/SidebarAnnos.tsx | 29 +++++++++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 7 +-- src/client/views/linking/LinkMenuItem.tsx | 18 +++---- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/MapBox/MapBox.tsx | 59 +++++++--------------- .../views/nodes/MapboxMapBox/MapboxContainer.tsx | 51 ++++++++++--------- src/client/views/nodes/PDFBox.tsx | 17 +++---- src/client/views/nodes/VideoBox.tsx | 17 ++++--- src/client/views/nodes/WebBox.tsx | 26 +++++----- .../views/nodes/formattedText/FormattedTextBox.tsx | 17 +++---- 13 files changed, 124 insertions(+), 126 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts index 36e03daed..41a61b154 100644 --- a/src/client/documents/DocUtils.ts +++ b/src/client/documents/DocUtils.ts @@ -724,7 +724,7 @@ export namespace DocUtils { _width: width || BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 * 6 : 200, _height: BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 : 35, _layout_autoHeight: true, - backgroundColor: StrCast(Doc.UserDoc().textBackgroundColor), + backgroundColor: backgroundColor ?? StrCast(Doc.UserDoc().textBackgroundColor), borderColor: Doc.UserDoc().borderColor as string, borderWidth: Doc.UserDoc().borderWidth as number, text_centered: BoolCast(Doc.UserDoc().textCentered), diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index fc8f22cf3..422613968 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -250,7 +250,7 @@ export class DocumentManager { const options = optionsIn; Doc.MyRecentlyClosed && Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, targetDoc); const docContextPath = DocumentManager.GetContextPath(targetDoc, true); - if (docContextPath.some(doc => doc.hidden)) options.toggleTarget = false; + if (docContextPath.some(doc => doc !== targetDoc && doc.hidden)) options.toggleTarget = false; let activatedTab = false; if (DocumentView.activateTabView(docContextPath[0])) { options.toggleTarget = false; diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index b2e42652d..d354dd2c0 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -194,14 +194,13 @@ export class MarqueeAnnotator extends ObservableReactComponent { e.preventDefault(); e.stopPropagation(); - const sourceAnchorCreator = () => this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true); // hyperlink color - const targetCreator = (annotationOn: Doc | undefined) => { const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, annotationOn, 'yellow'); target.layout_fitWidth = true; DocumentView.SetSelectOnLoad(target); return target; }; + const sourceAnchorCreator = () => this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true); // hyperlink color DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView(), sourceAnchorCreator, targetCreator), e.pageX, e.pageY, { dragComplete: dragEv => { if (!dragEv.aborted && dragEv.annoDragData && dragEv.annoDragData.linkSourceDoc && dragEv.annoDragData.dropDocument && dragEv.linkDocument) { diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 573c28ccf..71b479a22 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -3,7 +3,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { ClientUtils, returnFalse, returnOne, returnZero } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc, DocListCast, Field, FieldResult, FieldType, StrListCast } from '../../fields/Doc'; +import { Doc, DocListCast, Field, FieldResult, FieldType, Opt, StrListCast } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { List } from '../../fields/List'; import { RichTextField } from '../../fields/RichTextField'; @@ -19,6 +19,7 @@ import { StyleProp } from './StyleProp'; import { CollectionStackingView } from './collections/CollectionStackingView'; import { DocumentView } from './nodes/DocumentView'; import { FieldViewProps } from './nodes/FieldView'; +import { FocusViewOptions } from './nodes/FocusViewOptions'; interface ExtraProps { fieldKey: string; @@ -149,15 +150,33 @@ export class SidebarAnnos extends ObservableReactComponent { if (DocListCast(this._props.Doc[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) { - if (this.childFilters()) { + if (this.childFilters().length) { // if any child filters exist, get rid of them this._props.layoutDoc._childFilters = new List(); + return true; } - return true; } return false; }; + public static getView(sidebar: SidebarAnnos | null, sidebarShown: boolean, toggleSidebar: () => void, doc: Doc, options: FocusViewOptions) { + if (!sidebarShown) { + options.didMove = true; + toggleSidebar(); + } + options.didMove = sidebar?.makeDocUnfiltered(doc) || options.didMove; + + if (!doc.hidden) { + if (!options.didMove && options.toggleTarget) { + options.toggleTarget = false; + options.didMove = doc.hidden = true; + } + } else { + options.didMove = !(doc.hidden = false); + } + return new Promise>(res => DocumentView.addViewRenderedCb(doc, res)); + } + get sidebarKey() { return this._props.fieldKey + '_sidebar'; } @@ -181,8 +200,8 @@ export class SidebarAnnos extends ObservableReactComponent 'title'; setHeightCallback = (height: number) => this._props.setHeight?.(height + this.filtersHeight()); sortByLinkAnchorY = (a: Doc, b: Doc) => { - const ay = Doc.Links(a).length && DocCast(Doc.Links(a)[0].link_anchor_1).y; - const by = Doc.Links(b).length && DocCast(Doc.Links(b)[0].link_anchor_1).y; + const ay = Doc.Links(a).length && DocCast(Doc.Links(a)[0].link_anchor_1)?.y; + const by = Doc.Links(b).length && DocCast(Doc.Links(b)[0].link_anchor_1)?.y; return NumCast(ay) - NumCast(by); }; render() { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index bb3c59eae..77615c650 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1738,10 +1738,11 @@ export class CollectionFreeFormView extends CollectionSubView([anchor]); + this.dataDoc[fieldKey] = new List([anchor]); } } return anchor; diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index c984a7053..6c1ad568d 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -105,11 +105,7 @@ export class LinkMenuItem extends ObservableReactComponent { LinkManager.Instance.currentLinkAnchor = LinkManager.Instance.currentLink ? this.sourceAnchor : undefined; if ((SnappingManager.PropertiesWidth ?? 0) < 100) { - setTimeout( - action(() => { - SnappingManager.SetPropertiesWidth(250); - }) - ); + setTimeout(action(() => SnappingManager.SetPropertiesWidth(250))); } } }) @@ -136,14 +132,14 @@ export class LinkMenuItem extends ObservableReactComponent { this._props.itemHandler?.(this._props.linkDoc); } else { const focusDoc = - Cast(this._props.linkDoc.link_anchor_1, Doc, null)?.annotationOn === this._props.sourceDoc - ? Cast(this._props.linkDoc.link_anchor_1, Doc, null) - : Cast(this._props.linkDoc.link_anchor_2, Doc, null)?.annotationOn === this._props.sourceDoc - ? Cast(this._props.linkDoc.link_anchor_12, Doc, null) - : undefined; + DocCast(this._props.linkDoc.link_anchor_1)?.annotationOn === this._props.sourceDoc + ? DocCast(this._props.linkDoc.link_anchor_1) + : DocCast(this._props.linkDoc.link_anchor_2)?.annotationOn === this._props.sourceDoc + ? DocCast(this._props.linkDoc.link_anchor_2) + : undefined; // prettier-ignore if (focusDoc) this._props.docView._props.focus(focusDoc, { instant: true }); - DocumentView.FollowLink(this._props.linkDoc, this._props.sourceDoc, false); + DocumentView.FollowLink(this._props.linkDoc, focusDoc ?? this._props.sourceDoc, false); } } ); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 05706fe6b..b0415ef18 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1412,7 +1412,7 @@ export class DocumentView extends DocComponent() { } public static setDefaultImageTemplate(checkResult?: boolean) { if (checkResult) return Doc.UserDoc().defaultImageLayout; - const view = DocumentView.Selected()[0]?._props.renderDepth > 0 ? DocumentView.Selected()[0] : undefined; + const view = DocumentView.Selected()[0]?._props.renderDepth > 0 || DocumentView.Selected()[0]?.Document.isTemplateDoc ? DocumentView.Selected()[0] : undefined; undoable(() => { const tempDoc = DocumentView.getTemplate(view); Doc.UserDoc().defaultImageLayout = tempDoc ? new PrefetchProxy(tempDoc) : undefined; diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index a563b7c1b..a279ccc48 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -13,7 +13,7 @@ import { CirclePicker, ColorResult } from 'react-color'; import { Layer, MapProvider, MapRef, Map as MapboxMap, Marker, Source, ViewState, ViewStateChangeEvent } from 'react-map-gl/mapbox'; import { ClientUtils, setupMoveUpEvents } from '../../../../ClientUtils'; import { emptyFunction } from '../../../../Utils'; -import { Doc, DocListCast, Field, LinkedTo, Opt, StrListCast } from '../../../../fields/Doc'; +import { Doc, DocListCast, Field, LinkedTo, StrListCast } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; import { RichTextField } from '../../../../fields/RichTextField'; import { DocCast, NumCast, StrCast, toList } from '../../../../fields/Types'; @@ -111,12 +111,12 @@ export class MapBox extends ViewBoxAnnotatableComponent() { // this list contains pushpins and configs @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); } // prettier-ignore - @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.SidebarKey]); } // prettier-ignore + @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.sidebarKey]); } // prettier-ignore @computed get allPushpins() { return this.allAnnotations.filter(anno => anno.type === DocumentType.PUSHPIN); } // prettier-ignore @computed get allRoutes() { return this.allAnnotations.filter(anno => anno.type === DocumentType.MAPROUTE); } // prettier-ignore @computed get SidebarShown() { return !!this.layoutDoc._layout_showSidebar; } // prettier-ignore @computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%'); } // prettier-ignore - @computed get SidebarKey() { return this.fieldKey + '_sidebar'; } // prettier-ignore + @computed get sidebarKey() { return this.fieldKey + '_sidebar'; } // prettier-ignore @computed get sidebarColor() { return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this._props.fieldKey + '_backgroundColor'], '#e4e4e4')); } @@ -260,7 +260,7 @@ export class MapBox extends ViewBoxAnnotatableComponent() { removeMapDocument = (doc: Doc | Doc[], annotationKey?: string) => { this.allAnnotations - .filter(anno => toList(doc).includes(DocCast(anno.mapPin))) + .filter(anno => toList(doc).includes(DocCast(anno.mapPin)!)) .forEach(anno => { anno.mapPin = undefined; }); @@ -339,27 +339,19 @@ export class MapBox extends ViewBoxAnnotatableComponent() { startAnchorDrag = (e: PointerEvent, ele: HTMLElement) => { e.preventDefault(); e.stopPropagation(); - - const sourceAnchorCreator = action(() => { - const note = this.getAnchor(true); - if (note && this._selectedPinOrRoute) { - note.latitude = this._selectedPinOrRoute.latitude; - note.longitude = this._selectedPinOrRoute.longitude; - note.map = this._selectedPinOrRoute.map; - } - return note as Doc; - }); - const targetCreator = (annotationOn: Doc | undefined) => { const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow'); + target.layout_fitWidth = true; DocumentView.SetSelectOnLoad(target); return target; }; + + const sourceAnchorCreator = () => this.getAnchor(true); const docView = this.DocumentView?.(); docView && DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, { dragComplete: dragEv => { - if (!dragEv.aborted && dragEv.annoDragData && dragEv.annoDragData.linkSourceDoc && dragEv.annoDragData.dropDocument && dragEv.linkDocument) { + if (!dragEv.aborted && dragEv.annoDragData?.linkSourceDoc && dragEv.annoDragData.dropDocument && dragEv.linkDocument) { dragEv.annoDragData.linkSourceDoc.followLinkToggle = dragEv.annoDragData.dropDocument.annotationOn === this.Document; dragEv.annoDragData.linkSourceDoc.followLinkZoom = false; } @@ -368,17 +360,7 @@ export class MapBox extends ViewBoxAnnotatableComponent() { }; createNoteAnnotation = () => { - const createFunc = undoable( - action(() => { - const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(true), ['latitude', 'longitude', LinkedTo]); - if (note && this._selectedPinOrRoute) { - note.latitude = this._selectedPinOrRoute.latitude; - note.longitude = this._selectedPinOrRoute.longitude; - note.map = this._selectedPinOrRoute.map; - } - }), - 'create note annotation' - ); + const createFunc = undoable(() => this._sidebarRef.current?.anchorMenuClick(this.getAnchor(true), ['latitude', 'longitude', LinkedTo]), 'create note annotation'); if (!this.layoutDoc.layout_showSidebar) { this.toggleSidebar(); setTimeout(createFunc); @@ -428,14 +410,11 @@ export class MapBox extends ViewBoxAnnotatableComponent() { } }; - getView = (doc: Doc, options: FocusViewOptions) => { - if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) { - this.toggleSidebar(); - options.didMove = true; + getView = async (doc: Doc, options: FocusViewOptions) => { + if (DocListCast(this.dataDoc[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) { + SidebarAnnos.getView(this._sidebarRef.current, this.SidebarShown, this.toggleSidebar, doc, options); } - return new Promise>(res => { - DocumentView.addViewRenderedCb(doc, dv => res(dv)); - }); + return undefined; }; /* @@ -476,13 +455,14 @@ export class MapBox extends ViewBoxAnnotatableComponent() { @action deleteSelectedPinOrRoute = undoable(() => { - if (this._selectedPinOrRoute) { + const selPin = DocCast(this._selectedPinOrRoute); + if (selPin) { // Removes filter - Doc.setDocFilter(this.Document, 'latitude', NumCast(this._selectedPinOrRoute.latitude), 'remove'); - Doc.setDocFilter(this.Document, 'longitude', NumCast(this._selectedPinOrRoute.longitude), 'remove'); - Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this._selectedPinOrRoute))}`, 'remove'); + Doc.setDocFilter(this.Document, 'latitude', NumCast(selPin.latitude), 'remove'); + Doc.setDocFilter(this.Document, 'longitude', NumCast(selPin.longitude), 'remove'); + Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(selPin)}`, 'remove'); - this.removePushpinOrRoute(this._selectedPinOrRoute); + this.removePushpinOrRoute(selPin); } MapAnchorMenu.Instance.fadeOut(true); document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true); @@ -1299,7 +1279,6 @@ export class MapBox extends ViewBoxAnnotatableComponent() { removeMapDocument = (docsIn: Doc | Doc[], annotationKey?: string) => { const docs = toList(docsIn); this.allAnnotations - .filter(anno => docs.includes(DocCast(anno.mapPin))) + .filter(anno => docs.includes(DocCast(anno.mapPin)!)) .forEach(anno => { anno.mapPin = undefined; }); @@ -224,6 +224,12 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent startAnchorDrag = (e: PointerEvent, ele: HTMLElement) => { e.preventDefault(); e.stopPropagation(); + const targetCreator = (annotationOn: Doc | undefined) => { + const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow'); + target.layout_fitWidth = true; + DocumentView.SetSelectOnLoad(target); + return target; + }; const sourceAnchorCreator = action(() => { const note = this.getAnchor(true); @@ -235,11 +241,6 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent return note as Doc; }); - const targetCreator = (annotationOn: Doc | undefined) => { - const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow'); - DocumentView.SetSelectOnLoad(target); - return target; - }; const docView = this.DocumentView?.(); docView && DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, { @@ -362,22 +363,22 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent @action deselectPin = () => { - if (this.selectedPin) { + const selPin = DocCast(this.selectedPin); + if (selPin) { // Removes filter - Doc.setDocFilter(this.Document, 'latitude', NumCast(this.selectedPin.latitude), 'remove'); - Doc.setDocFilter(this.Document, 'longitude', NumCast(this.selectedPin.longitude), 'remove'); - Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove'); + Doc.setDocFilter(this.Document, 'latitude', NumCast(selPin.latitude), 'remove'); + Doc.setDocFilter(this.Document, 'longitude', NumCast(selPin.longitude), 'remove'); + Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(selPin)}`, 'remove'); - const temp = this.selectedPin; if (!this._unmounting) { - this._bingMap.current.entities.remove(this.map_docToPinMap.get(temp)); + this._bingMap.current.entities.remove(this.map_docToPinMap.get(selPin)); } - const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(temp.latitude, temp.longitude)); - this.MicrosoftMaps.Events.addHandler(newpin, 'click', () => this.pushpinClicked(temp as Doc)); + const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(selPin.latitude, selPin.longitude)); + this.MicrosoftMaps.Events.addHandler(newpin, 'click', () => this.pushpinClicked(selPin)); if (!this._unmounting) { this._bingMap.current.entities.push(newpin); } - this.map_docToPinMap.set(temp, newpin); + this.map_docToPinMap.set(selPin, newpin); this.selectedPin = undefined; this.bingSearchBarContents = this.Document.map; } @@ -388,9 +389,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent this.toggleSidebar(); options.didMove = true; } - return new Promise>(res => { - DocumentView.addViewRenderedCb(doc, dv => res(dv)); - }); + return new Promise>(res => DocumentView.addViewRenderedCb(doc, res)); }; /* * Pushpin onclick @@ -535,13 +534,14 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent @action deleteSelectedPin = undoable(() => { - if (this.selectedPin) { + const selPin = this.selectedPin; + if (selPin) { // Removes filter - Doc.setDocFilter(this.Document, 'latitude', NumCast(this.selectedPin.latitude), 'remove'); - Doc.setDocFilter(this.Document, 'longitude', NumCast(this.selectedPin.longitude), 'remove'); - Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove'); + Doc.setDocFilter(this.Document, 'latitude', NumCast(selPin.latitude), 'remove'); + Doc.setDocFilter(this.Document, 'longitude', NumCast(selPin.longitude), 'remove'); + Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(selPin)}`, 'remove'); - this.removePushpin(this.selectedPin); + this.removePushpin(selPin); } MapAnchorMenu.Instance.fadeOut(true); document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true); @@ -638,7 +638,10 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent this._disposers.highlight = reaction( () => this.allAnnotations.map(doc => doc[Highlight]), () => { - const allConfigPins = this.allAnnotations.map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })).filter(pair => pair.pushpin); + const allConfigPins = this.allAnnotations + .map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })) + .filter(pair => pair.pushpin) + .map(pair => ({ doc: pair.doc, pushpin: pair.pushpin! })); allConfigPins.forEach(({ pushpin }) => { if (!pushpin[Highlight] && this.map_pinHighlighted.get(pushpin)) { this.recolorPin(pushpin); diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 55e6d5596..e4050a3c3 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -10,7 +10,7 @@ import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { ComputedField } from '../../../fields/ScriptField'; -import { Cast, FieldValue, NumCast, StrCast, toList } from '../../../fields/Types'; +import { Cast, DocCast, FieldValue, NumCast, StrCast, toList } from '../../../fields/Types'; import { ImageField, PdfField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; @@ -27,7 +27,6 @@ import { Colors } from '../global/globalEnums'; import { PDFViewer } from '../pdf/PDFViewer'; import { PinDocView, PinProps } from '../PinFuncs'; import { SidebarAnnos } from '../SidebarAnnos'; -import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { FocusViewOptions } from './FocusViewOptions'; import { ImageBox } from './ImageBox'; @@ -56,6 +55,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent() { @observable private _pdf: Opt = undefined; @observable private _pageControls = false; + @computed get sidebarKey() { + return this.fieldKey + '_sidebar'; + } @computed get pdfUrl() { return Cast(this.dataDoc[this._props.fieldKey], PdfField); } @@ -232,14 +234,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent() { return this._pdfViewer?.scrollFocus(anchor, NumCast(anchor.y, NumCast(anchor.config_scrollTop)), options); }; - getView = (doc: Doc, options: FocusViewOptions) => { - if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) { - options.didMove = true; - this.toggleSidebar(false); + getView = async (doc: Doc, options: FocusViewOptions) => { + if (DocListCast(this.dataDoc[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) { + SidebarAnnos.getView(this._sidebarRef.current, this.SidebarShown, () => this.toggleSidebar(false), doc, options); } - return new Promise>(res => { - DocumentView.addViewRenderedCb(doc, dv => res(dv)); - }); + return undefined; }; getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index fa099178c..b3cb0e1db 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -338,12 +338,17 @@ export class VideoBox extends ViewBoxAnnotatableComponent() { getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { const timecode = Cast(this.layoutDoc._layout_currentTimecode, 'number', null); const marquee = AnchorMenu.Instance.GetAnchor?.(undefined, addAsAnnotation); + const docAnchor = () => + Docs.Create.ConfigDocument({ + title: '#' + timecode, + _timecodeToShow: timecode, + annotationOn: this.Document, + }); if (!addAsAnnotation && marquee) marquee.backgroundColor = 'transparent'; - const anchor = - addAsAnnotation && marquee - ? CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this.annotationKey, timecode || undefined, undefined, marquee, addAsAnnotation) || this.Document - : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.Document }); + const visibleAnchor = () => addAsAnnotation && marquee && (CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this.annotationKey, timecode || undefined, undefined, marquee, addAsAnnotation) || this.Document); + const anchor = visibleAnchor() || docAnchor(); PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true, pannable: true } }, this.Document); + addAsAnnotation && this.addDocument(anchor); return anchor; }; @@ -376,9 +381,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent() { } return this._stackedTimeline.getView(doc, options); } - return new Promise>(res => { - DocumentView.addViewRenderedCb(doc, dv => res(dv)); - }); + return new Promise>(res => DocumentView.addViewRenderedCb(doc, res)); }; // extracts video thumbnails and saves them as field of doc diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 838dbea9d..779ccab40 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -13,7 +13,7 @@ import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { RefField } from '../../../fields/RefField'; import { listSpec } from '../../../fields/Schema'; -import { Cast, NumCast, StrCast, toList, WebCast } from '../../../fields/Types'; +import { Cast, DocCast, NumCast, StrCast, toList, WebCast } from '../../../fields/Types'; import { ImageField, WebField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, stringHash } from '../../../Utils'; @@ -104,6 +104,9 @@ export class WebBox extends ViewBoxAnnotatableComponent() { @computed get webField() { return Cast(this.Document[this._props.fieldKey], WebField)?.url; } + @computed get sidebarKey() { + return this.fieldKey + '_sidebar'; + } constructor(props: FieldViewProps) { super(props); @@ -308,18 +311,17 @@ export class WebBox extends ViewBoxAnnotatableComponent() { }; @action - getView = (doc: Doc /* , options: FocusViewOptions */) => { - if (Doc.AreProtosEqual(doc, this.Document)) - return new Promise>(res => { - res(this.DocumentView?.()); - }); + getView = async (doc: Doc, options: FocusViewOptions) => { + if (Doc.AreProtosEqual(doc, this.Document)) return new Promise>(res => res(this.DocumentView?.())); + if (this.Document.layout_fieldKey === 'layout_icon') this.DocumentView?.().iconify(); const webUrl = WebCast(doc.config_data)?.url; if (this._url && webUrl && webUrl.href !== this._url) this.setData(webUrl.href); - if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false); - return new Promise>(res => { - DocumentView.addViewRenderedCb(doc, dv => res(dv)); - }); + + if (DocListCast(this.dataDoc[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) { + return SidebarAnnos.getView(this._sidebarRef.current, this.SidebarShown, () => this.toggleSidebar(false), doc, options); + } + return undefined; }; sidebarAddDocTab = (doc: Doc, where: OpenWhere) => { @@ -454,7 +456,7 @@ export class WebBox extends ViewBoxAnnotatableComponent() { iframeDown = (e: PointerEvent) => { this._textAnnotationCreator = undefined; const sel = this._url ? this._iframe?.contentDocument?.getSelection() : window.document.getSelection(); - if (sel?.empty && !(e.target as any).textContent) + if (sel?.empty && !(e.target as HTMLElement).textContent) sel.empty(); // Chrome else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox @@ -565,7 +567,7 @@ export class WebBox extends ViewBoxAnnotatableComponent() { 'click', undoable( action((e: MouseEvent) => { - let eleHref = (e.target as any)?.outerHTML?.split('"="')[1]?.split('"')[0]; + let eleHref = (e.target as HTMLElement)?.outerHTML?.split('"="')[1]?.split('"')[0]; for (let ele = e.target as HTMLElement | Element | null; ele; ele = ele.parentElement) { if ('href' in ele) { eleHref = (typeof ele.href === 'string' ? ele.href : eleHref) || (ele.parentElement && 'href' in ele.parentElement ? (ele.parentElement.href as string) : eleHref); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 57720baae..bab97f681 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -270,13 +270,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn); + const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow'); + target.layout_fitWidth = true; DocumentView.SetSelectOnLoad(target); return target; }; + const sourceAnchorCreator = () => this.getAnchor(true); + const docView = this.DocumentView?.(); - docView && DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, () => this.getAnchor(true), targetCreator), e.pageX, e.pageY); + docView && DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY); }); AnchorMenu.Instance.AddDrawingAnnotation = (drawing: Doc) => { @@ -1070,15 +1073,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { if (DocListCast(this.dataDoc[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) { - if (!this.SidebarShown) { - this.toggleSidebar(false); - options.didMove = true; - } - setTimeout(() => this._sidebarRef?.current?.makeDocUnfiltered(doc)); + return SidebarAnnos.getView(this._sidebarRef.current, this.SidebarShown, () => this.toggleSidebar(false), doc, options); } - return new Promise>(res => { - DocumentView.addViewRenderedCb(doc, dv => res(dv)); - }); + return new Promise>(res => DocumentView.addViewRenderedCb(doc, res)); }; focus = (textAnchor: Doc, options: FocusViewOptions) => { const focusSpeed = options.zoomTime ?? 500; -- cgit v1.2.3-70-g09d2