Browse Source

safe mode not fully done

David `Deltakosh` Catuhe 5 years ago
parent
commit
335d232e05

File diff suppressed because it is too large
+ 13 - 0
Playground/imgs/diff.svg


Playground/old/css/img/inspectorButton.svg → Playground/imgs/inspector.svg


File diff suppressed because it is too large
+ 1 - 0
Playground/imgs/options.svg


+ 43 - 3
Playground/src/components/commandBarComponent.tsx

@@ -1,6 +1,7 @@
 import * as React from "react";
 import { GlobalState } from '../globalState';
 import { CommandButtonComponent } from './commandButtonComponent';
+import { CommandDropdownComponent } from './commandDropdownComponent';
 
 require("../scss/commandBar.scss");
 
@@ -34,14 +35,53 @@ export class CommandBarComponent extends React.Component<ICommandBarComponentPro
         this.props.globalState.onDownloadRequiredObservable.notifyObservers();
     }
 
+    onInspector() {
+        this.props.globalState.onInspectorRequiredObservable.notifyObservers();
+    }
+
     public render() {
         return (
             <div className={"commands " + (this.props.globalState.language === "JS" ? "background-js" : "background-ts")}>
-                <CommandButtonComponent globalState={this.props.globalState} tooltip="Run" icon="play" isActive={true} onClick={()=> this.onPlay()}/>
-                <CommandButtonComponent globalState={this.props.globalState} tooltip="Save" icon="save" isActive={false} onClick={()=> this.onSave()}/>
-                <CommandButtonComponent globalState={this.props.globalState} tooltip="Download" icon="download" isActive={false} onClick={()=> this.onDownload()}/>
+                <CommandButtonComponent globalState={this.props.globalState} tooltip="Run" icon="play" shortcut="Alt+Enter" isActive={true} onClick={()=> this.onPlay()}/>
+                <CommandButtonComponent globalState={this.props.globalState} tooltip="Save" icon="save" shortcut="Ctrl+S" isActive={false} onClick={()=> this.onSave()}/>
+                <CommandButtonComponent globalState={this.props.globalState} tooltip="Inspector" icon="inspector" isActive={false} onClick={()=> this.onInspector()}/>
+                <CommandButtonComponent globalState={this.props.globalState} tooltip="Download" icon="download" shortcut="Shift+Ctrl+S"isActive={false} onClick={()=> this.onDownload()}/>
                 <CommandButtonComponent globalState={this.props.globalState} tooltip="Create new" icon="new" isActive={false} onClick={()=> this.onNew()}/>
                 <CommandButtonComponent globalState={this.props.globalState} tooltip="Clear code" icon="clear" isActive={false} onClick={()=> this.onClear()}/>
+                <CommandDropdownComponent globalState={this.props.globalState} icon="options" tooltip="Options" items={[
+                    {
+                        label: "Safe mode",
+                        storeKey: "safe-mode",
+                        onCheck: () => {}
+                    },                     
+                    {
+                        label: "CTRL+S to save",
+                        storeKey: "ctrl-s-to-save",
+                        onCheck: () => {}
+                    }, 
+                    {
+                        label: "editor",
+                        storeKey: "editor",
+                        onCheck: (value) => {this.props.globalState.onEditorDisplayChangedObservable.notifyObservers(value)}
+                    }, {
+                        label: "minimap",
+                        storeKey: "minimap",
+                        onCheck: (value) => {this.props.globalState.onMinimapChangedObservable.notifyObservers(value)}
+                    }, {
+                        label: "fullscreen",
+                        onClick: () => {this.props.globalState.onFullcreenRequiredObservable.notifyObservers()}
+                    },                     {
+                        label: "fullscreen editor",
+                        onClick: () => {this.props.globalState.onEditorFullcreenRequiredObservable.notifyObservers()}
+                    },                   {
+                        label: "format code",
+                        onClick: () => {this.props.globalState.onFormatCodeRequiredObservable.notifyObservers()}
+                    },
+                    {
+                        label: "metadata",
+                        onClick: () => {this.props.globalState.onDisplayMetadataObservable.notifyObservers(true)}
+                    }
+                ]}/>
             </div>
         );
     }

+ 2 - 1
Playground/src/components/commandButtonComponent.tsx

@@ -4,6 +4,7 @@ import { GlobalState } from '../globalState';
 interface ICommandButtonComponentProps {
     globalState: GlobalState;
     tooltip: string;   
+    shortcut?: string;
     icon: string; 
     isActive: boolean;
     onClick: () => void;
@@ -17,7 +18,7 @@ export class CommandButtonComponent extends React.Component<ICommandButtonCompon
 
     public render() {
         return (
-            <div className="command-button" onClick={this.props.onClick} title={this.props.tooltip}>
+            <div className="command-button" onClick={this.props.onClick} title={this.props.tooltip + (this.props.shortcut ? "\n" + this.props.shortcut : "")}>
                 <img src={"imgs/" + this.props.icon + ".svg"} className={this.props.isActive ? "active" : ""}/>
                 <div className="command-label">
                     {this.props.tooltip}

+ 72 - 0
Playground/src/components/commandDropdownComponent.tsx

@@ -0,0 +1,72 @@
+import * as React from "react";
+import { GlobalState } from '../globalState';
+import { Utilities } from '../tools/utilities';
+
+interface ICommandDropdownComponentProps {
+    globalState: GlobalState;
+    icon: string; 
+    tooltip: string;
+    items: {label: string, onClick?: () => void, onCheck?: (value: boolean) => void, storeKey?: string}[]
+}
+
+export class CommandDropdownComponent extends React.Component<ICommandDropdownComponentProps, {isExpanded: boolean}> {    
+  
+    public constructor(props: ICommandDropdownComponentProps) {
+        super(props);
+
+        this.state = {isExpanded: false}
+    }    
+
+    public render() {
+        return (
+            <>
+                {
+                    this.state.isExpanded &&
+                    <div className="command-dropdown-blocker" onClick={() => this.setState({isExpanded: false})}>
+                    </div>
+                }
+                <div className="command-dropdown-root">
+                    <div className={"command-dropdown" + (this.state.isExpanded ? " activated" : "")} title={this.props.tooltip} onClick={() => this.setState({isExpanded: !this.state.isExpanded})}>
+                        <img src={"imgs/" + this.props.icon + ".svg"}/>
+                    </div>
+                    {
+                            this.state.isExpanded &&
+                            <div className="command-dropdown-content sub1">
+                                {
+                                    this.props.items.map(m => {
+                                        return (
+                                            <div className="command-dropdown-label" key={m.label} onClick={() => {
+                                                if (! m.onClick) {
+                                                    let newValue = !Utilities.ReadBoolFromStore(m.storeKey!);
+                                                    Utilities.StoreBoolFromStore(m.storeKey!, newValue);
+                                                    this.forceUpdate();
+                                                    m.onCheck!(newValue);
+                                                    return;
+                                                }
+                                                m.onClick();
+                                                this.setState({isExpanded: false});
+                                            }} title={m.label}>
+                                                <div className="command-dropdown-label-text">
+                                                    {m.label}
+                                                </div>
+                                                {
+                                                    m.onCheck && 
+                                                    <input type="checkBox" className="command-dropdown-label-check" 
+                                                        onChange={(evt) => {
+                                                            Utilities.StoreBoolFromStore(m.storeKey!, evt.target.checked);
+                                                            this.forceUpdate();
+                                                            m.onCheck!(evt.target.checked);
+                                                        }}
+                                                        checked={Utilities.ReadBoolFromStore(m.storeKey!)}/>
+                                                }
+                                            </div>
+                                        )
+                                    })
+                                }
+                            </div>
+                        }
+                </div>
+            </>
+        );
+    }
+}

+ 36 - 8
Playground/src/components/hamburgerMenu.tsx

@@ -2,47 +2,75 @@ import * as React from "react";
 import { GlobalState } from '../globalState';
 import { CommandButtonComponent } from './commandButtonComponent';
 
+import HambugerButton from "../imgs/hamburger.svg";
+
 require("../scss/hamburgerMenu.scss");
 
 interface IHamburgerMenuComponentProps {
     globalState: GlobalState;
 }
 
-export class HamburgerMenuComponent extends React.Component<IHamburgerMenuComponentProps> {    
+export class HamburgerMenuComponent extends React.Component<IHamburgerMenuComponentProps, {isExpanded: boolean}> {    
   
     public constructor(props: IHamburgerMenuComponentProps) {
         super(props);
+        this.state = {isExpanded: false};
     }    
 
     onPlay() {
         this.props.globalState.onRunRequiredObservable.notifyObservers();
+        this.setState({isExpanded: false});
     }
 
     onNew() {
         this.props.globalState.onNewRequiredObservable.notifyObservers();
+        this.setState({isExpanded: false});
     }
 
     onClear() {        
         this.props.globalState.onClearRequiredObservable.notifyObservers();
+        this.setState({isExpanded: false});
     }
 
     onSave() {
         this.props.globalState.onSaveRequiredObservable.notifyObservers();
+        this.setState({isExpanded: false});
     }
 
     onDownload() {
         this.props.globalState.onDownloadRequiredObservable.notifyObservers();
+        this.setState({isExpanded: false});
+    }
+
+    onInspector() {
+        this.props.globalState.onInspectorRequiredObservable.notifyObservers();
+        this.setState({isExpanded: false});
+    }
+
+    switch() {
+        this.setState({isExpanded: !this.state.isExpanded});
     }
 
     public render() {
         return (
-            <div className={"hambuger-menu " + (this.props.globalState.language === "JS" ? "background-js" : "background-ts")}>
-                <CommandButtonComponent globalState={this.props.globalState} tooltip="Run" icon="play" isActive={true} onClick={()=> this.onPlay()}/>
-                <CommandButtonComponent globalState={this.props.globalState} tooltip="Save" icon="save" isActive={false} onClick={()=> this.onSave()}/>
-                <CommandButtonComponent globalState={this.props.globalState} tooltip="Download" icon="download" isActive={false} onClick={()=> this.onDownload()}/>
-                <CommandButtonComponent globalState={this.props.globalState} tooltip="Create new" icon="new" isActive={false} onClick={()=> this.onNew()}/>
-                <CommandButtonComponent globalState={this.props.globalState} tooltip="Clear code" icon="clear" isActive={false} onClick={()=> this.onClear()}/>
-            </div>
+            <>
+                {
+                    this.state.isExpanded && 
+                    <div className="click-blocker" onClick={() => this.setState({isExpanded: false})}>                        
+                    </div>
+                }
+                <div className={"hamburger-button " + (this.props.globalState.language === "JS" ? "background-js" : "background-ts")} onClick={() => this.switch()}>
+                    <HambugerButton />
+                </div>
+                <div className={"hambuger-menu " + (this.props.globalState.language === "JS" ? "background-js" : "background-ts") + (this.state.isExpanded ? " expanded" : "")}>
+                    <CommandButtonComponent globalState={this.props.globalState} tooltip="Run" icon="play" isActive={true} onClick={()=> this.onPlay()}/>
+                    <CommandButtonComponent globalState={this.props.globalState} tooltip="Save" icon="save" isActive={false} onClick={()=> this.onSave()}/>
+                    <CommandButtonComponent globalState={this.props.globalState} tooltip="Download" icon="download" isActive={false} onClick={()=> this.onDownload()}/>
+                    <CommandButtonComponent globalState={this.props.globalState} tooltip="Create new" icon="new" isActive={false} onClick={()=> this.onNew()}/>
+                    <CommandButtonComponent globalState={this.props.globalState} tooltip="Clear code" icon="clear" isActive={false} onClick={()=> this.onClear()}/>
+                    <CommandButtonComponent globalState={this.props.globalState} tooltip="Inspector" icon="inspector" isActive={false} onClick={()=> this.onInspector()}/>
+                </div>
+            </>
         );
     }
 }

+ 2 - 2
Playground/src/components/headerComponent.tsx

@@ -26,10 +26,10 @@ export class HeaderComponent extends React.Component<IHeaderComponentProps> {
     
     public render() {
         return (
-            <div id="header">   
+            <div id="pg-header">   
                 <div className="logo-area">
                     <LogoImage />
-                    <div className="version">Playground&nbsp;<span className="version-number" ref={this._refVersionNumber}></span></div>
+                    <div className="version"><div className="version-text">Playground&nbsp;</div><span className="version-number" ref={this._refVersionNumber}></span></div>
                 </div>
                 <div className="command-bar">
                     {

+ 18 - 16
Playground/src/components/metadataComponent.tsx

@@ -71,22 +71,24 @@ export class MetadataComponent extends React.Component<IMetadataComponentProps,
         }
 
         return (
-            <div id="metadata-editor" className={(this.props.globalState.language === "JS" ? "background-js" : "background-ts")}>
-                <label htmlFor="title">TITLE</label>
-                <div className="separator"></div>
-                <input type="text" maxLength={120} id="title" className="save-form-title" ref={this._titleRef} value={this.props.globalState.currentSnippetTitle}/>
-
-                <label htmlFor="description">DESCRIPTION</label>
-                <div className="separator"></div>
-                <textarea id="description" rows={4} cols={10} ref={this._descriptionRef} value={this.props.globalState.currentSnippetDescription}></textarea>
-
-                <label htmlFor="tags">TAGS (separated by comma)</label>
-                <div className="separator"></div>
-                <textarea id="tags" rows={4} cols={10} ref={this._tagsRef} value={this.props.globalState.currentSnippetTags}></textarea>
-
-                <div className="editor-buttons" id="buttons">
-                    <div id="ok" onClick={() => this.onOk()}>OK</div>
-                    <div id="cancel" onClick={() => this.onCancel()}>Cancel</div>
+            <div id="metadata-editor-root">
+                <div id="metadata-editor" className={(this.props.globalState.language === "JS" ? "background-js" : "background-ts")}>
+                    <label htmlFor="title">TITLE</label>
+                    <div className="separator"></div>
+                    <input type="text" maxLength={120} id="title" className="save-form-title" ref={this._titleRef} value={this.props.globalState.currentSnippetTitle}/>
+
+                    <label htmlFor="description">DESCRIPTION</label>
+                    <div className="separator"></div>
+                    <textarea id="description" rows={4} cols={10} ref={this._descriptionRef} value={this.props.globalState.currentSnippetDescription}></textarea>
+
+                    <label htmlFor="tags">TAGS (separated by comma)</label>
+                    <div className="separator"></div>
+                    <textarea id="tags" rows={4} cols={10} ref={this._tagsRef} value={this.props.globalState.currentSnippetTags}></textarea>
+
+                    <div className="editor-buttons" id="buttons">
+                        <div id="ok" onClick={() => this.onOk()}>OK</div>
+                        <div id="cancel" onClick={() => this.onCancel()}>Cancel</div>
+                    </div>
                 </div>
             </div>
         )

+ 11 - 0
Playground/src/components/monacoComponent.tsx

@@ -16,6 +16,17 @@ export class MonacoComponent extends React.Component<IMonacoComponentProps> {
         super(props);
 
         this._monacoManager = new MonacoManager(this.props.globalState);
+
+        this.props.globalState.onEditorFullcreenRequiredObservable.add(() => {
+            let editorDiv = this.props.refObject.current! as any;
+            if (editorDiv.requestFullscreen) {
+                editorDiv.requestFullscreen();
+            } else if (editorDiv.mozRequestFullScreen) {
+                editorDiv.mozRequestFullScreen();
+            } else if (editorDiv.webkitRequestFullscreen) {
+                editorDiv.webkitRequestFullscreen();
+            }
+        });
     }
     
     componentDidMount() {        

+ 19 - 0
Playground/src/components/rendererComponent.tsx

@@ -40,6 +40,25 @@ export class RenderingComponent extends React.Component<IRenderingComponentProps
             }
             this._downloadManager.download(this._engine);
         });
+
+        this.props.globalState.onInspectorRequiredObservable.add(() => {
+            if (!this._scene) {
+                return;
+            }
+
+            if (this._scene.debugLayer.isVisible()) {
+                this._scene.debugLayer.hide();
+            } else {
+                this._scene.debugLayer.show({
+                    embedMode: true
+                });
+            }
+        });
+
+        this.props.globalState.onFullcreenRequiredObservable.add(() => {
+            this._engine?.switchFullscreen(false);
+        });
+
     }
 
     compileAndRun() {

+ 6 - 0
Playground/src/globalState.ts

@@ -33,6 +33,12 @@ export class GlobalState {
     public onMetadataUpdatedObservable = new Observable<void>();  
     public onMetadataWindowHiddenObservable = new Observable<boolean>();
     public onDownloadRequiredObservable = new Observable<void>();
+    public onInspectorRequiredObservable = new Observable<void>();    
+    public onFormatCodeRequiredObservable = new Observable<void>();  
+    public onFullcreenRequiredObservable = new Observable<void>();
+    public onEditorFullcreenRequiredObservable = new Observable<void>();
+    public onMinimapChangedObservable = new Observable<boolean>();
+    public onEditorDisplayChangedObservable = new Observable<boolean>();
 
     public loadingCodeInProgress = false;
     public onCodeLoaded = new Observable<string>();

+ 1 - 0
Playground/src/imgs/hamburger.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55 55"><defs><style>.cls-1{fill:none;}</style></defs><title>hamburgerButton</title><g id="UI"><rect x="18.46" y="20.1" width="17.99" height="2.36"/><rect x="18.46" y="26.34" width="17.99" height="2.36"/><rect x="18.46" y="32.59" width="17.99" height="2.36"/></g><g id="Icon_buttons" data-name="Icon buttons"><rect class="cls-1" width="55" height="55"/></g></svg>

+ 21 - 7
Playground/src/playground.tsx

@@ -10,6 +10,8 @@ import { LoadManager } from './tools/loadManager';
 import { WaitRingComponent } from './components/waitRingComponent';
 import { MetadataComponent } from './components/metadataComponent';
 import { HamburgerMenuComponent } from './components/hamburgerMenu';
+import { Utilities } from './tools/utilities';
+import { ShortcutManager } from './tools/shortcutManager';
 
 require("./scss/main.scss");
 const Split = require('split.js').default;
@@ -27,6 +29,7 @@ export class Playground extends React.Component<IPlaygroundProps, {errorMessage:
     
     public saveManager: SaveManager;
     public loadManager: LoadManager;
+    public shortcutManager: ShortcutManager;
     
     public constructor(props: IPlaygroundProps) {
        super(props);
@@ -36,19 +39,27 @@ export class Playground extends React.Component<IPlaygroundProps, {errorMessage:
        this.monacoRef = React.createRef();
        this.renderingRef = React.createRef();
 
-       this.state = {errorMessage: "", mode: window.innerWidth < this._globalState.MobileSizeTrigger ? this._globalState.mobileDefaultMode : EditionMode.Desktop};
+       let defaultDesktop = Utilities.ReadBoolFromStore("editor") ? EditionMode.Desktop : EditionMode.RenderingOnly;
+
+       this.state = {errorMessage: "", mode: window.innerWidth < this._globalState.MobileSizeTrigger ? this._globalState.mobileDefaultMode : defaultDesktop};
 
        window.addEventListener("resize", () => {
-           this.setState({mode: window.innerWidth < this._globalState.MobileSizeTrigger ? this._globalState.mobileDefaultMode : EditionMode.Desktop});
+            let defaultDesktop = Utilities.ReadBoolFromStore("editor") ? EditionMode.Desktop : EditionMode.RenderingOnly;
+           this.setState({mode: window.innerWidth < this._globalState.MobileSizeTrigger ? this._globalState.mobileDefaultMode : defaultDesktop});
        });
 
        this._globalState.onMobileDefaultModeChangedObservable.add(() => {
            this.setState({mode: this._globalState.mobileDefaultMode});
        });
 
+       this._globalState.onEditorDisplayChangedObservable.add(value => {
+        this.setState({mode: value ? EditionMode.Desktop : EditionMode.RenderingOnly});
+       });
+
        // Managers
        this.saveManager = new SaveManager(this._globalState);
        this.loadManager = new LoadManager(this._globalState);
+       this.shortcutManager = new ShortcutManager(this._globalState);
     }
 
     componentDidMount() {
@@ -93,15 +104,18 @@ export class Playground extends React.Component<IPlaygroundProps, {errorMessage:
     public render() {
 
         return (
-            <div id="root">              
+            <div id="pg-root">              
                 <HeaderComponent globalState={this._globalState}/>
-                <div ref={this.splitRef} id="split">
-                    <MonacoComponent globalState={this._globalState} className="split-part" refObject={this.monacoRef}/>    
-                    <div ref={this.renderingRef} className="split-part">
+                <div ref={this.splitRef} id="pg-split">
+                    <MonacoComponent globalState={this._globalState} className="pg-split-part" refObject={this.monacoRef}/>    
+                    <div ref={this.renderingRef} className="pg-split-part">
                         <RenderingComponent globalState={this._globalState}/>
                     </div>
                 </div>
-                <HamburgerMenuComponent globalState={this._globalState}/>
+                {
+                    window.innerWidth < 1024 &&
+                    <HamburgerMenuComponent globalState={this._globalState}/>
+                }
                 <FooterComponent globalState={this._globalState}/>                
                 <WaitRingComponent globalState={this._globalState}/>
                 <MetadataComponent globalState={this._globalState}/>

+ 80 - 0
Playground/src/scss/commandBar.scss

@@ -30,4 +30,84 @@
             transform: scale(0.95);
         }
     }
+
+    .command-dropdown-root {
+        position: relative;
+        text-transform: uppercase;
+        z-index: 1;
+    }
+
+    .command-dropdown {        
+        cursor: pointer;
+        width: 55px;
+        height: 55px;
+
+        img {
+            filter: invert(57%) sepia(80%) saturate(2031%) hue-rotate(215deg);
+            &.active {
+                filter: invert(100%);
+            }
+        }
+
+        &:hover, &.activated {
+            background-color: white;
+            img {
+                filter: invert(17%) !important;
+            }
+        } 
+        
+        &:active {
+            transform-origin: center;
+            transform: scale(0.95);
+        }
+    }
+
+    .command-dropdown-blocker {
+        position: absolute;    
+        top:0;
+        left:0;
+        width: 100%;
+        height: 100%;
+        transform: scale(1);
+        z-index: 1;
+    }
+
+    .command-dropdown-content {
+        position: absolute;
+        top: 55px;
+        width: 200px;
+        transform: scale(1);
+        z-index: 100;
+
+        .command-dropdown-label {
+            font-family: "acumin-pro-extra-condensed";
+            color:white;
+            padding: 5px;
+            padding-left: 10px;
+            height: 35px;
+            font-size: 20px;
+            display: grid;
+            align-items: center;
+            cursor: pointer;
+            user-select: none;
+            grid-template-columns: 1fr 20px;
+            grid-template-rows: 100%;
+
+            &:hover {
+                background-color: white;
+                color: black;
+            } 
+
+            .command-dropdown-label-text {
+                grid-column: 1;
+                grid-row: 1;
+            }
+
+            .command-dropdown-label-check {
+                grid-column: 2;
+                grid-row: 1;
+            }
+        }
+    }
+
 }

+ 1 - 1
Playground/src/scss/footer.scss

@@ -1,7 +1,7 @@
 #footer {
     width: 100%;
     height: 100%;
-    grid-column: 2;
+    grid-column: 1 / 3;
     grid-row: 3;
     padding: 0;
     margin: 0;

+ 45 - 3
Playground/src/scss/hamburgerMenu.scss

@@ -1,17 +1,58 @@
-.hambuger-menu {
+.hamburger-button {
+    grid-row: 1;
+    grid-column: 1;
+    width: 40px;
+    color:white;
+    cursor: pointer;
+    transform: scale(1);
+    
+    &:hover {
+        background-color: white;
+        svg {
+            fill: black;
+        }
+    } 
+    
+    &:active {
+        transform-origin: center;
+        transform: scale(0.95);
+    }
+
+    svg {
+        fill: white;
+    }
+}
+
+.click-blocker {
     grid-row: 1 / 4;
+    grid-column: 1 / 4;
+    background: transparent;
+    transform: scale(1);
+}
+
+.hambuger-menu {
+    grid-row: 1 / 2;
     grid-column: 1;
     display: flex;
     flex-direction: column;
     position: absolute;
     left:0;
-    top:55px;
+    top:40px;
+    height: calc(100% - 70px);
+    transform: translateX(-200px);
+    opacity: 0;
+    transition: all 0.2s ease;
+
+    &.expanded {
+        transform: translateX(0);
+        opacity: 1;
+    }
 
     .command-button {
         cursor: pointer;
         width: 200px;
         display: grid;
-        grid-template-columns: 55px 1fr;
+        grid-template-columns: 40px 1fr;
         grid-template-rows: 100%;
 
         .command-label {
@@ -19,6 +60,7 @@
             grid-column: 2;
             grid-row: 1;
             align-self: center;
+            text-transform: uppercase;
         }
         
         img {

+ 38 - 2
Playground/src/scss/header.scss

@@ -1,4 +1,4 @@
-#header {
+#pg-header {
     width: 100%;
     height: 100%;
     grid-column: 2;
@@ -74,4 +74,40 @@
             }
         }
     }
-}
+}
+
+
+@media screen and (max-width: 1280px) {
+    #pg-header {
+        .logo-area {
+            width: 300px;
+                
+            .version {
+                .version-text {
+                    display: none;
+                }
+            }
+        }
+    }
+}
+
+@media screen and (max-width: 1024px) {
+    #pg-header {
+        grid-template-columns: 100%;
+
+        .logo-area {
+            width: 100%;
+                
+            .version {
+                .version-text {
+                    display: none;
+                }
+            }
+        }
+
+        
+        .command-bar {  
+            display: none;
+        }
+    }
+}

+ 14 - 4
Playground/src/scss/main.scss

@@ -1,4 +1,4 @@
-#root {
+#pg-root {
     width: 100%;
     height: 100%;
     padding: 0;
@@ -11,17 +11,17 @@
     grid-template-rows: 55px 1fr 35px;
 }
 
-#split {
+#pg-split {
     width: 100%;
     height: 100%;
     padding: 0;
     margin: 0;
     display: flex;
     grid-row: 2;
-    grid-column: 2;
+    grid-column: 1 / 3;
     overflow: hidden;
 
-    .split-part {
+    .pg-split-part {
         width: 100%;
         height: 100%;
         padding: 0;
@@ -45,4 +45,14 @@
 
 .background-js {
     background-color: #3f3461;
+
+    .sub1 {
+        background-color:#9379e6
+    }
+}
+
+@media screen and (max-width: 1024px) {
+    #pg-root {
+        grid-template-rows: 40px 1fr 35px;
+    }
 }

+ 46 - 37
Playground/src/scss/metadata.scss

@@ -1,46 +1,55 @@
-#metadata-editor {
-    grid-column: 1;
+#metadata-editor-root {
+    grid-column: 1 / 3;
     grid-row: 1 / 3;
+    width: 100%;
+    height: 100%;
+    z-index: 1;
 
-    position: absolute;
-    top: 80px;
-    left: calc(50% - 205px);
-    width: 410px;
-    height: 370px;
-    padding-top: 15px;
-    -webkit-border-radius: 4px;
-    -moz-border-radius: 4px;
-    border-radius: 4px;
-    color: white;
-    font-size: 14px;
-    text-align: center;
 
-    .separator {
-        width: 350px;
-        border-bottom: 1px solid #999;
-        margin: auto;
-        margin-bottom: 10px;
-    }
+    #metadata-editor {
 
-    textarea, input {
-        display: block;
-        width: 350px;
-        margin: auto;
-        margin-bottom: 20px;
-        padding: 5px;
-        resize: none;
-    }
+        position: absolute;
+        top: 80px;
+        left: calc(50% - 205px);
+        width: 410px;
+        height: 390px;
+        padding-top: 15px;
+        -webkit-border-radius: 4px;
+        -moz-border-radius: 4px;
+        border-radius: 4px;
+        color: white;
+        font-size: 14px;
+        text-align: center;
+
+        .separator {
+            width: 350px;
+            border-bottom: 1px solid #999;
+            margin: auto;
+            margin-bottom: 10px;
+        }
+
+        textarea, input {
+            display: block;
+            width: 350px;
+            margin: auto;
+            margin-bottom: 20px;
+            padding: 5px;
+            resize: none;
+        }
 
-    .editor-buttons{
-        user-select: none;
-         div {
-            cursor: pointer;
-            display: inline-block;
-            width: 100px;
+        .editor-buttons{
+            user-select: none;
+            div {
+                cursor: pointer;
+                display: inline-block;
+                width: 100px;
+                height: 30px;
+                padding-top: 10px;
 
-            &:hover {
-                background: white;
-                color: black;
+                &:hover {
+                    background: white;
+                    color: black;
+                }
             }
         }
     }

+ 1 - 1
Playground/src/scss/waitRing.scss

@@ -9,7 +9,7 @@
 }
 
 #wait-ring {
-    grid-column: 1;
+    grid-column: 1 / 3;
     grid-row: 1 / 3;
     width: 100%;
     height: 100%;

+ 38 - 5
Playground/src/tools/monacoManager.ts

@@ -6,6 +6,7 @@ import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
 import * as languageFeatures from "monaco-editor/esm/vs/language/typescript/languageFeatures";
 
 import { GlobalState } from '../globalState';
+import { Utilities } from './utilities';
 
 declare type IStandaloneCodeEditor = import('monaco-editor/esm/vs/editor/editor.api').editor.IStandaloneCodeEditor;
 declare type IStandaloneEditorConstructionOptions = import('monaco-editor/esm/vs/editor/editor.api').editor.IStandaloneEditorConstructionOptions;
@@ -24,13 +25,25 @@ export class MonacoManager {
         insertTextRules: number}[];
 
     public constructor(public globalState: GlobalState) {
+        window.addEventListener('beforeunload', (evt) => {
+            if (Utilities.ReadBoolFromStore("safe-mode")) {
+                var message = 'Are you sure you want to leave. You have unsaved work.';
+                evt.preventDefault();
+                evt.returnValue = message;
+            }
+        });
+
         globalState.onNewRequiredObservable.add(() => {
-            this._setNewContent();
+            if (this._checkSafeMode("Are you sure you want to create a new playground?")) {
+                this._setNewContent();
+            }
         });
 
-        globalState.onClearRequiredObservable.add(() => {
-            this._editor?.setValue("");
-            location.hash = "";
+        globalState.onClearRequiredObservable.add(() => {            
+            if (this._checkSafeMode("Are you sure you want to remove all your code?")) {
+                this._editor?.setValue("");
+                location.hash = "";
+            }
         });
 
         globalState.onCodeLoaded.add(code => {
@@ -43,6 +56,18 @@ export class MonacoManager {
             this.globalState.onRunRequiredObservable.notifyObservers();
         });
 
+        globalState.onFormatCodeRequiredObservable.add(() => {
+            this._editor?.getAction('editor.action.formatDocument').run();
+        });
+
+        globalState.onMinimapChangedObservable.add(value => {
+            this._editor?.updateOptions({
+                minimap: {
+                    enabled: value
+                }
+            });
+        });
+
         // Register a global observable for inspector to request code changes
         let pgConnect = {
             onRequestCodeChangeObservable: new BABYLON.Observable()
@@ -81,6 +106,14 @@ export class MonacoManager {
         }        
     }
 
+    private _checkSafeMode(message: string) {
+        if (Utilities.ReadBoolFromStore("safe-mode")) {
+            return window.confirm(message);
+        }
+
+        return true;
+    };
+
     public async setupMonacoAsync(hostElement: HTMLDivElement) {
         let response = await fetch("https://preview.babylonjs.com/babylon.d.ts");
         if (!response.ok) {
@@ -110,7 +143,7 @@ export class MonacoManager {
             showFoldingControls: "always",
             renderIndentGuides: true,
             minimap: {
-                enabled: true
+                enabled: Utilities.ReadBoolFromStore("minimap")
             }
         };      
 

+ 43 - 0
Playground/src/tools/shortcutManager.ts

@@ -0,0 +1,43 @@
+import { GlobalState } from '../globalState';
+import { Utilities } from './utilities';
+
+export class ShortcutManager {
+
+    public constructor(public globalState: GlobalState) {  
+        this._register();
+    }
+
+    private _register() {
+        // HotKeys
+        document.onkeydown = (e) => {
+            // Alt+Enter to Run
+            if (e.altKey && (e.keyCode === 13)) {
+                this.globalState.onRunRequiredObservable.notifyObservers();
+                return;
+            }
+            
+            // Ctrl+Shift+S to Download Zip
+            if (
+                (window.navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) &&
+                e.shiftKey &&
+                (e.keyCode === 83)
+            ) {
+                this.globalState.onDownloadRequiredObservable.notifyObservers();
+                return;
+            }
+
+            // Ctrl+S to Save
+            if (
+                (window.navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) &&
+                (e.keyCode === 83)
+            ) {
+                e.preventDefault();
+                if (!Utilities.ReadBoolFromStore("ctrl-s-to-save")) {
+                    return;
+                }
+                this.globalState.onSaveRequiredObservable.notifyObservers();
+                return;
+            }
+        };
+    }
+}

+ 8 - 0
Playground/src/tools/utilities.ts

@@ -22,4 +22,12 @@ export class Utilities {
         }
         return query;
     }
+
+    public static ReadBoolFromStore(key: string): boolean {
+        return localStorage.getItem(key) === "true";
+    }
+
+    public static StoreBoolFromStore(key: string, value: boolean): void {
+        localStorage.setItem(key, value ? "true" : "false");
+    }
 }