diff options
Diffstat (limited to 'src/client/views')
| -rw-r--r-- | src/client/views/nodes/imageEditor/ImageEditorButtons.tsx | 3 | ||||
| -rw-r--r-- | src/client/views/nodes/trails/PresBox.tsx | 295 | ||||
| -rw-r--r-- | src/client/views/nodes/trails/SpringUtils.ts | 9 |
3 files changed, 111 insertions, 196 deletions
diff --git a/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx b/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx index e810881a5..985dc914f 100644 --- a/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx +++ b/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx @@ -53,9 +53,8 @@ export function ApplyFuncButtons({ loading, onClick: getEdit, onReset, btnText } export function ImageToolButton(tool: ImageEditTool, isActive: boolean, selectTool: (type: ImageToolType) => void) { return ( - <div className="imageEditorButtonContainer"> + <div key={tool.name} className="imageEditorButtonContainer"> <Button - key={tool.name} style={{ width: '100%' }} text={tool.name} type={Type.TERT} diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index e0de8e1fd..3919ac3a8 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -1,4 +1,4 @@ -import { Button, Dropdown, DropdownType, IconButton, Toggle, ToggleType, Type } from '@dash/components'; +import { Button, Dropdown, DropdownType, IconButton, Size, Toggle, ToggleType, Type } from '@dash/components'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; import Slider from '@mui/material/Slider'; @@ -61,6 +61,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } static navigateToDocScript: ScriptField; + public static PanelName = 'PRESBOX'; // name of dockingview tab where presentations get added + constructor(props: FieldViewProps) { super(props); makeObservable(this); @@ -72,6 +74,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { private _disposers: { [name: string]: IReactionDisposer } = {}; public selectedArray = new ObservableSet<Doc>(); + public slideToModify: Doc | null = null; _batch: UndoManager.Batch | undefined = undefined; // undo batch for dragging sliders which generate multiple scene edit events as the cursor moves _keyTimer: NodeJS.Timeout | undefined; // timer for turning off transition flag when key frame change has completed. Need to clear this if you do a second navigation before first finishes, or else first timer can go off during second naviation. _unmounting = false; // flag that view is unmounting used to block RemFromMap from deleting things @@ -97,14 +100,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @observable _presKeyEvents: boolean = false; @observable _forceKeyEvents: boolean = false; + @observable _showBezierEditor = false; + @observable _showSpringEditor = true; + @observable _showPreview = true; + @observable _easeDropdownVal = 'ease'; + // GPT - private _inputref: HTMLTextAreaElement | null = null; - private _inputref2: HTMLTextAreaElement | null = null; - @observable chatActive: boolean = false; - @observable chatInput: string = ''; - public slideToModify: Doc | null = null; - @observable isRecording: boolean = false; - @observable isLoading: boolean = false; + @observable _chatActive: boolean = false; + @observable _animationChat: string = ''; + @observable _chatInput: string = ''; + @observable _isRecording: boolean = false; + @observable _isLoading: boolean = false; @observable generatedAnimations: AnimationSettings[] = [ // default presets @@ -138,54 +144,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { }, ]; - @action - setGeneratedAnimations = (settings: AnimationSettings[]) => { - this.generatedAnimations = settings; - }; - - @observable animationChat: string = ''; - - @action - setChatInput = (input: string) => { - this.chatInput = input; - }; - - @action - setAnimationChat = (input: string) => { - this.animationChat = input; - }; - - @action - setIsLoading = (isLoading: boolean) => { - this.isLoading = isLoading; - }; - - @action - public setIsRecording = (isRecording: boolean) => { - this.isRecording = isRecording; - }; - - @observable showBezierEditor = false; - @action setBezierEditorVisibility = (visible: boolean) => { - this.showBezierEditor = visible; - }; - @observable showSpringEditor = true; - @action setSpringEditorVisibility = (visible: boolean) => { - this.showSpringEditor = visible; - }; - - // Easing function variables - - @observable easeDropdownVal = 'ease'; - - @action setBezierControlPoints = (newPoints: { p1: number[]; p2: number[] }) => { + setGeneratedAnimations = action((input: AnimationSettings[]) => { this.generatedAnimations = input; }) // prettier-ignore + setChatInput = action((input: string) => { this._chatInput = input; }); // prettier-ignore + setAnimationChat = action((input: string) => { this._animationChat = input; }); // prettier-ignore + setIsLoading = action((input?: boolean) => { this._isLoading = !!input; }); // prettier-ignore + setIsRecording = action((input: boolean) => { this._isRecording = input; }); // prettier-ignore + setBezierEditorVisibility = action((visible: boolean) => { this._showBezierEditor = visible; }); // prettier-ignore + setSpringEditorVisibility = action((visible: boolean) => { this._showSpringEditor = visible; }); // prettier-ignore + setBezierControlPoints = action((newPoints: { p1: number[]; p2: number[] }) => { this.setEaseFunc(this.activeItem, `cubic-bezier(${newPoints.p1[0]}, ${newPoints.p1[1]}, ${newPoints.p2[0]}, ${newPoints.p2[1]})`); - }; + }); @computed get currCPoints() { - const strPoints = this.activeItem.presentation_easeFunc ? StrCast(this.activeItem.presentation_easeFunc) : 'ease'; - return EaseFuncToPoints(strPoints); + return EaseFuncToPoints(this.activeItem.presentation_easeFunc ? StrCast(this.activeItem.presentation_easeFunc) : 'ease'); } @computed @@ -221,25 +193,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { if ([DocumentType.PDF, DocumentType.WEB, DocumentType.RTF].includes(this.targetDoc.type as DocumentType) || this.targetDoc._type_collection === CollectionViewType.Stacking) return true; return false; } - @computed get panable() { - if ((this.targetDoc.type === DocumentType.COL && this.targetDoc._type_collection === CollectionViewType.Freeform) || this.targetDoc.type === DocumentType.IMG) return true; - return false; - } @computed get selectedDocumentView() { if (DocumentView.Selected().length) return DocumentView.Selected()[0]; if (this.selectedArray.size) return DocumentView.getDocumentView(this.Document); return undefined; } - @computed get isPres() { - return this.selectedDoc === this.Document; - } - @computed get selectedDoc() { - return this.selectedDocumentView?.Document; - } - isActiveItemTarget = (layoutDoc: Doc) => this.activeItem?.presentation_targetDoc === layoutDoc; - clearSelectedArray = () => this.selectedArray.clear(); - addToSelectedArray = action((doc: Doc) => this.selectedArray.add(doc)); - removeFromSelectedArray = action((doc: Doc) => this.selectedArray.delete(doc)); componentWillUnmount() { this._unmounting = true; @@ -256,7 +214,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { () => this.pauseAutoPres() ); this._disposers.keyboard = reaction( - () => this.selectedDoc, + () => this.selectedDocumentView?.Document, selected => { document.removeEventListener('keydown', PresBox.keyEventsWrapper, true); (this._presKeyEvents = selected === this.Document) && document.addEventListener('keydown', PresBox.keyEventsWrapper, true); @@ -293,13 +251,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { ); } + clearSelectedArray = () => this.selectedArray.clear(); + addToSelectedArray = action((doc: Doc) => this.selectedArray.add(doc)); + removeFromSelectedArray = action((doc: Doc) => this.selectedArray.delete(doc)); + @action updateCurrentPresentation = (pres?: Doc) => { Doc.ActivePresentation = pres ?? this.Document; PresBox.Instance = this; }; - _mediaTimer!: [NodeJS.Timeout, Doc]; // 'Play on next' for audio or video therefore first navigate to the audio/video before it should be played startTempMedia = (targetDoc: Doc, activeItem: Doc) => { const duration: number = NumCast(activeItem.config_clipEnd) - NumCast(activeItem.config_clipStart); @@ -318,7 +279,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { }; // Recording for GPT customization - recordDictation = () => { this.setIsRecording(true); this.setChatInput(''); @@ -336,64 +296,34 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { DictationManager.Controls.stop(); }; - setDictationContent = (value: string) => { - console.log('Dictation value', value); - this.setChatInput(value); - }; + setDictationContent = (value: string) => this.setChatInput(value); - @action - customizeAnimations = async () => { + customizeAnimations = action(() => { this.setIsLoading(true); - try { - const res = await getSlideTransitionSuggestions(this.animationChat); - if (typeof res === 'string') { - const resObj = JSON.parse(res); - console.log('Parsed GPT Result ', resObj); - this.setGeneratedAnimations(resObj as AnimationSettings[]); - } - } catch (err) { - console.error(err); - } - this.setIsLoading(false); - }; + getSlideTransitionSuggestions(this._animationChat) + .then(res => this.setGeneratedAnimations(JSON.parse(res) as AnimationSettings[])) + .catch(err => console.error(err)) + .finally(this.setIsLoading); + }); - @action - customizeWithGPT = async (input: string) => { + customizeWithGPT = action((input: string) => { // const testInput = 'change title to Customized Slide, transition for 2.3s with fade in effect'; this.setIsRecording(false); this.setIsLoading(true); - - const currSlideProperties: { [key: string]: FieldResult } = {}; - gptSlideProperties.forEach(key => { - if (this.activeItem[key]) { - currSlideProperties[key] = this.activeItem[key]; - } - // default values - else if (key === 'presentation_transition') { - currSlideProperties[key] = 500; - } else if (key === 'config_zoom') { - currSlideProperties[key] = 1.0; - } - }); - console.log('current slide props ', currSlideProperties); - - try { - const res = await gptTrailSlideCustomization(input, currSlideProperties); - if (typeof res === 'string') { - const resObj = JSON.parse(res); - console.log('Parsed GPT Result ', resObj); - for (const key in resObj) { - if (resObj[key]) { - console.log('typeof property', typeof resObj[key]); - this.activeItem[key] = resObj[key]; - } - } - } - } catch (err) { - console.error(err); - } - this.setIsLoading(false); - }; + const slideDefaults: { [key: string]: FieldResult } = { presentation_transition: 500, config_zoom: 1 }; + const currSlideProperties = gptSlideProperties.reduce( + (prev, key) => { prev[key] = Field.toString(this.activeItem[key]) ?? prev[key]; return prev; }, + slideDefaults); // prettier-ignore + + gptTrailSlideCustomization(input, JSON.stringify(currSlideProperties)) + .then(res => + (Object.entries(JSON.parse(res)) as string[][]).forEach(([key, val]) => { + this.activeItem[key] = (+val).toString() === val ? +val : (val ?? this.activeItem[key]); + }) + ) + .catch(e => console.error(e)) + .finally(this.setIsLoading); + }); // TODO: al: it seems currently that tempMedia doesn't stop onslidechange after clicking the button; the time the tempmedia stop depends on the start & end time // TODO: to handle child slides (entering into subtrail and exiting), also the next() and back() functions @@ -847,8 +777,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { PresBox.NavigateToTarget(targetDoc, activeItem, resetSelection); }; - public static PanelName = 'PRESBOX'; - static NavigateToTarget(targetDoc: Doc, activeItem: Doc, finished?: (options: FocusViewOptions) => void) { if (activeItem.presentation_movement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) { (DocumentView.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.(); @@ -1842,10 +1770,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { <ReactTextareaAutosize placeholder="Describe how you would like to modify the slide properties." className="pres-chatbox" - value={this.chatInput} - onChange={e => { - this.setChatInput(e.target.value); - }} + value={this._chatInput} + onChange={e => this.setChatInput(e.target.value)} onKeyDown={e => { this.stopDictation(); e.stopPropagation(); @@ -1853,11 +1779,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { /> <IconButton type={Type.TERT} - color={this.isRecording ? '#2bcaff' : SnappingManager.userVariantColor} + color={this._isRecording ? '#2bcaff' : SnappingManager.userVariantColor} tooltip="Record" icon={<BiMicrophone size="16px" />} onClick={() => { - if (!this.isRecording) { + if (!this._isRecording) { this.recordDictation(); } else { this.stopDictation(); @@ -1869,12 +1795,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { style={{ alignSelf: 'flex-end' }} text="Send" type={Type.TERT} - icon={this.isLoading ? <ReactLoading type="spin" color="#ffffff" width={20} height={20} /> : <AiOutlineSend />} + icon={this._isLoading ? <ReactLoading type="spin" color="#ffffff" width={20} height={20} /> : <AiOutlineSend />} iconPlacement="right" color={SnappingManager.userVariantColor} onClick={() => { this.stopDictation(); - this.customizeWithGPT(this.chatInput); + this.customizeWithGPT(this._chatInput); }} /> </div> @@ -1952,16 +1878,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { style={{ alignSelf: 'flex-start' }} onClick={e => { e.stopPropagation(); - this.setBezierEditorVisibility(!this.showBezierEditor); + this.setBezierEditorVisibility(!this._showBezierEditor); }}> - {`${this.showBezierEditor ? 'Hide' : 'Show'} Timing Editor`} - <FontAwesomeIcon icon={this.showBezierEditor ? 'chevron-up' : 'chevron-down'} /> + {`${this._showBezierEditor ? 'Hide' : 'Show'} Timing Editor`} + <FontAwesomeIcon icon={this._showBezierEditor ? 'chevron-up' : 'chevron-down'} /> </div> </div> </div> {/* Cubic bezier editor */} - {this.showBezierEditor && ( + {this._showBezierEditor && ( <div className="presBox-option-block" style={{ paddingTop: 0 }}> <p className="presBox-submenu-label" style={{ alignSelf: 'flex-start' }}> Custom Timing Function @@ -1978,7 +1904,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { <ReactTextareaAutosize placeholder="Customize prompt for effect suggestions. Leave blank for random results." className="pres-chatbox" - value={this.animationChat} + value={this._animationChat} onChange={e => { this.setAnimationChat(e.target.value); }} @@ -1992,7 +1918,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { style={{ alignSelf: 'flex-end' }} text="Send" type={Type.TERT} - icon={this.isLoading ? <ReactLoading type="spin" color="#ffffff" width={20} height={20} /> : <AiOutlineSend />} + icon={this._isLoading ? <ReactLoading type="spin" color="#ffffff" width={20} height={20} /> : <AiOutlineSend />} iconPlacement="right" color={SnappingManager.userVariantColor} onClick={this.customizeAnimations} @@ -2107,12 +2033,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { closeOnSelect items={effectTimings} selectedVal={timingConfig.type} - setSelectedVal={val => { - this.updateEffectTiming(activeItem, { - type: val as SpringType, - ...springMappings[val], - }); - }} + setSelectedVal={val => this.updateEffectTiming(activeItem, { type: val as SpringType, ...springMappings[val] })} dropdownType={DropdownType.SELECT} type={Type.TERT} /> @@ -2120,73 +2041,61 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { className="presBox-show-hide-dropdown" onClick={e => { e.stopPropagation(); - this.setSpringEditorVisibility(!this.showSpringEditor); + this.setSpringEditorVisibility(!this._showSpringEditor); }}> - {`${this.showSpringEditor ? 'Hide' : 'Show'} Spring Settings`} - <FontAwesomeIcon icon={this.showSpringEditor ? 'chevron-up' : 'chevron-down'} /> + {`${this._showSpringEditor ? 'Hide' : 'Show'} Spring Settings`} + <FontAwesomeIcon icon={this._showSpringEditor ? 'chevron-up' : 'chevron-down'} /> </div> - {this.showSpringEditor && ( + {this._showSpringEditor && ( <> <div>Tension</div> - <div - onPointerDown={e => { - e.stopPropagation(); - }}> - <Slider - min={1} - max={1000} - step={5} - size="small" + <div onPointerDown={e => e.stopPropagation()}> + {/* prettier-ignore */} + <Slider min={1} max={1000} step={5} size="small" value={timingConfig.stiffness} - onChange={(e, val) => { - if (!timingConfig) return; - this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, stiffness: val as number }); - }} + onChange={(e, val) => timingConfig && this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, stiffness: val as number })} valueLabelDisplay="auto" /> </div> <div>Damping</div> - <div - onPointerDown={e => { - e.stopPropagation(); - }}> - <Slider - min={1} - max={100} - step={1} - size="small" + <div onPointerDown={e => e.stopPropagation()}> + {/* prettier-ignore */} + <Slider min={1} max={100} step={1} size="small" value={timingConfig.damping} - onChange={(e, val) => { - if (!timingConfig) return; - this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, damping: val as number }); - }} + onChange={(e, val) => timingConfig && this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, damping: val as number })} valueLabelDisplay="auto" /> </div> <div>Mass</div> - <div - onPointerDown={e => { - e.stopPropagation(); - }}> - <Slider - min={1} - max={10} - step={1} - size="small" + <div onPointerDown={e => e.stopPropagation()}> + {/* prettier-ignore */} + <Slider min={1} max={10} step={1} size="small" value={timingConfig.mass} - onChange={(e, val) => { - if (!timingConfig) return; - this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, mass: val as number }); - }} + onChange={(e, val) => timingConfig && this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, mass: val as number })} valueLabelDisplay="auto" /> </div> - Preview Effect - <div className="presBox-option-block presBox-option-center"> - <div className="presBox-effect-container"> - <SlideEffect dir={direction} presEffect={effect} springSettings={timingConfig} infinite> - <div className="presBox-effect-demo-box" style={{ backgroundColor: springPreviewColors[0] }} /> - </SlideEffect> + <div style={{ display: 'flex', alignItems: 'center'}}> + <Button + type={Type.TERT} + tooltip="show preview of slide animation effect" + size={Size.SMALL} + color={SnappingManager.userColor} + background={'transparent'} + onClick={action(() => { + this._showPreview = false; + setTimeout(action(() => { this._showPreview = true; }) );// prettier-ignore + })} + text={'Preview of: ' + effect + ', ' + direction} + /> + <div className="presBox-option-block presBox-option-center"> + <div className="presBox-effect-container"> + {!this._showPreview ? null : ( + <SlideEffect dir={direction} presEffect={effect} springSettings={timingConfig}> + <div className="presBox-effect-demo-box" style={{ backgroundColor: springPreviewColors[0] }} /> + </SlideEffect> + )} + </div> </div> </div> </> @@ -3078,7 +2987,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } */} </div> {/* presbox chatbox */} - {this.chatActive && <div className="presBox-chatbox" />} + {this._chatActive && <div className="presBox-chatbox" />} </div> ); } diff --git a/src/client/views/nodes/trails/SpringUtils.ts b/src/client/views/nodes/trails/SpringUtils.ts index 73e1e14f1..044afbeb1 100644 --- a/src/client/views/nodes/trails/SpringUtils.ts +++ b/src/client/views/nodes/trails/SpringUtils.ts @@ -22,7 +22,14 @@ export interface SpringSettings { } // Overall config - +// Keeps these settings in sync with the AnimationSettings interface (used by gpt); +export enum AnimationSettingsProperties { + effect = 'effect', + direction = 'direction', + stiffness = 'stiffness', + damping = 'damping', + mass = 'mass', +} export interface AnimationSettings { effect: PresEffect; direction: PresEffectDirection; |
