aboutsummaryrefslogtreecommitdiff
path: root/src/client/views
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views')
-rw-r--r--src/client/views/nodes/imageEditor/ImageEditorButtons.tsx3
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx295
-rw-r--r--src/client/views/nodes/trails/SpringUtils.ts9
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;