diff --git a/CHANGELOG.md b/CHANGELOG.md index de400ba..d1a1baf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file. Each batch It is a summary of changes that would be pertinent to the end user of the PGEtinker website. For a comprehensive history of changes made to the project, please refer to the repository's commit history. +## 2024-06-17 + +- Fixed [Issue #98](https://github.com/Moros1138/PGEtinker/issues/98) +- Removed sponsor spot from navbar +- Added normalize.css +- Added link to Javid's youtube channel +- Changed navbar menu, made ready for mobile +- Added mobile menu +- Added settings dialog +- Changed dialog behavior, no more click anywhere! +- Added toast notifications +- Added Javid Mode to settings dialog +- Added editor.inlayHints to settings dialog +- Fixed browser tests, broke due to UI changes +- Changed screenshot fail graphic (Thanks TechnicJelle) +- Changed light theme, make dialogs easier on the eyes + ## 2024-06-10 - Added language client automatic reconnect diff --git a/app/Http/Controllers/PatreonController.php b/app/Http/Controllers/PatreonController.php index 0281030..a9bd00a 100644 --- a/app/Http/Controllers/PatreonController.php +++ b/app/Http/Controllers/PatreonController.php @@ -49,14 +49,20 @@ function get_supporters(Request $request) ], 500); } - $supporters = Redis::get("supporters"); - - if(isset($supporters)) + try + { + $supporters = Redis::get("supporters"); + if(isset($supporters)) + { + $supporters = json_decode($supporters); + return $supporters; + } + } + catch(Exception $e) { - $supporters = json_decode($supporters); - return $supporters; + Log::emergency("Patreon supporters cache failed. Redis"); } - + return $this->getPatreonNames(); } @@ -114,7 +120,15 @@ function getPatreonNames() } $supporters = ["supporters" => $supporters]; - Redis::set("supporters", json_encode($supporters, JSON_PRETTY_PRINT)); + + try + { + Redis::set("supporters", json_encode($supporters, JSON_PRETTY_PRINT)); + } + catch(Exception $e) + { + Log::emergency("failed to set supporters cache. Redis"); + } return $supporters; } diff --git a/package-lock.json b/package-lock.json index 9e4c487..bd2f28f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,9 @@ "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@~5.1.1", "monaco-editor-wrapper": "~5.1.1", "monaco-languageclient": "~8.4.0", + "normalize-css": "^2.3.1", "sass": "^1.75.0", + "toastify-js": "^1.12.0", "typescript": "^5.4.5", "vite": "^5.0", "vscode": "npm:@codingame/monaco-vscode-api" @@ -1074,12 +1076,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -1190,9 +1192,9 @@ "dev": true }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -1295,6 +1297,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/insert-css": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/insert-css/-/insert-css-0.0.0.tgz", + "integrity": "sha512-PwixL1rVtKkM1gz43tEHwZ2sUOYmWB5zk/9YiEmOxERqjfIkkMY4jwrl3v3e9NLzblEdkBuMLiTLm/0sHMx4qA==", + "dev": true + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1537,6 +1545,16 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/normalize-css": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/normalize-css/-/normalize-css-2.3.1.tgz", + "integrity": "sha512-70Lnkke3P+8ehu56S0M0yoUTgTve/rCrEncjdgPmKER/TLZMRa30rFLW7Yv3iGZldmGV4bGevGW47VOfZJbGyw==", + "deprecated": "see npm.im/normalize.css instead", + "dev": true, + "dependencies": { + "insert-css": "0.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1731,6 +1749,12 @@ "node": ">=8.0" } }, + "node_modules/toastify-js": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/toastify-js/-/toastify-js-1.12.0.tgz", + "integrity": "sha512-HeMHCO9yLPvP9k0apGSdPUWrUbLnxUKNFzgUoZp1PHCLploIX/4DSQ7V8H25ef+h4iO9n0he7ImfcndnN6nDrQ==", + "dev": true + }, "node_modules/tree-dump": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.1.tgz", diff --git a/package.json b/package.json index b766359..b602d98 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,9 @@ "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@~5.1.1", "monaco-editor-wrapper": "~5.1.1", "monaco-languageclient": "~8.4.0", + "normalize-css": "^2.3.1", "sass": "^1.75.0", + "toastify-js": "^1.12.0", "typescript": "^5.4.5", "vite": "^5.0", "vscode": "npm:@codingame/monaco-vscode-api" diff --git a/resources/css/app.scss b/resources/css/app.scss index 2c3f628..4496f9e 100644 --- a/resources/css/app.scss +++ b/resources/css/app.scss @@ -1,5 +1,6 @@ @import "./pixel-code.scss"; -@import "./loading.scss"; +@import "./app/loading.scss"; +@import "toastify-js/src/toastify.css"; body { --pgetinker-link-color: #ff6500; @@ -10,7 +11,7 @@ body { --pgetinker-text-shadow-color: #000; --pgetinker-menu-text-color: #ded; - --pgetinker-menu-background: #f1f1f1; + --pgetinker-menu-background: #3e3c44; --pgetinker-menu-hover-background:#a86326; --pgetinker-menu-hover-text-color: #ded; @@ -32,11 +33,11 @@ body.light { --pgetinker-background-color: #fff; --pgetinker-text-color:#000; - --pgetinker-text-shadow-color: #fff; + --pgetinker-text-shadow-color: none; + --pgetinker-menu-text-color: #424242; --pgetinker-menu-background: #f1f1f1; --pgetinker-menu-hover-background:#f70; - --pgetinker-menu-text-color: #424242; --pgetinker-menu-hover-text-color: #ded; --pgetinker-submenu-background: #f1f1f1; @@ -46,8 +47,8 @@ body.light { --pgetinker-content-border-color: #8c909d; - --pgetinker-dialog-background-color:#dee4ff; - --pgetinker-dialog-text-color:rgb(56, 56, 56); + --pgetinker-dialog-background-color:#fff; + --pgetinker-dialog-text-color:#000; --pgetinker-dialog-border-color: #444; } @@ -75,6 +76,10 @@ a:hover { text-decoration: underline; } +a:focus { + outline: none; +} + #app { position: absolute; top: 0; @@ -83,163 +88,56 @@ a:hover { height: 100%; background: var(--pgetinker-background-color); color: var(--pgetinker-text-color); -} - -#header { - position: absolute; - display: block; - top: 0; - left: 0; - width: 100%; - height: 48px; - z-index: 100; -} - -nav a.brand, -nav a.brand img { - display: block; - width: auto; - height: 48px; - float: left; -} - -nav a.brand:hover, -nav a.brand:link, -nav a.brand:active, -nav a.brand:visited { - display: inline-block; - line-height: 47px; - padding: 0 .8rem; - text-decoration: none; - font-size: 3rem; - color: var(--pgetinker-link-color); - text-shadow: .2rem .2rem .2rem #000; -} - - -a:active.menu-item, -a:link.menu-item, -a:visited.menu-item, -.menu-item { - display: inline-block; - background-color: var(--pgetinker-background-color); - color: var(--pgetinker-menu-text-color); - font-size: 1.2rem; - letter-spacing: 1px; - text-shadow: .2rem .2rem .5rem var(--pgetinker-text-shadow-color); - height: 48px; - line-height: 48px; - vertical-align: middle; - padding: 0 .4rem; - border: none; - cursor: pointer; - text-decoration: none; -} - - -.lucide { - vertical-align: middle; - filter: drop-shadow(.2rem .2rem .1rem var(--pgetinker-text-shadow-color)); -} - -nav .sponsor { - line-height: 47px; - padding: 0; - margin-right: .5rem; - font-size: 0.8rem; - text-shadow: .1rem .1rem .2rem var(--pgetinker-text-shadow-color); - white-space: nowrap; -} -.sponsor a { - text-decoration: none; + #header { + position: absolute; + display: block; + top: 0; + left: 0; + width: 100%; + height: 48px; + z-index: 100; + } + + #content { + position: absolute; + top: 49px; + bottom: 0px; + width: 100%; + overflow: hidden; + margin: 0; + padding: 0; + border-top: 1px solid var(--pgetinker-content-border-color); + border-bottom: 1px solid var(--pgetinker-content-border-color); + } } -.sponsor a:hover { - text-decoration: underline; -} - -.dropdown { - position: relative; - display: inline-block; -} - -.dropdown-content { - display: none; - position: absolute; - background-color: var(--pgetinker-submenu-background); - box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); - z-index: 1; - white-space: nowrap; - border-top: 1px solid var(--pgetinker-content-border-color); -} - -.dropdown-content a { - font-size: 1.2rem; - text-shadow: .2rem .2rem .5rem var(--pgetinker-text-shadow-color); - color: var(--pgetinker-submenu-text-color); - padding: 12px 16px; - text-decoration: none; - display: block; -} - -.dropdown-content a:hover { - background-color: var(--pgetinker-submenu-hover-background); -} - -.dropdown:hover .dropdown-content { - display: block; -} - -a:hover.menu-item, -.menu-item:hover, -.dropdown:hover .menu-item { - background-color: var(--pgetinker-menu-hover-background); -} - -/* differentiate between left menus and right menus */ -nav .navbar-left-menu { - float: left; -} - -nav .navbar-right-menu { - float: right; +.center { + text-align: center; } -nav .navbar-right-menu .dropdown-content { - right: 0; -} -nav .navbar-right-menu .menu-item { - margin-top: -3px; +.display-block { + display: block !important; } -#content { - position: absolute; - top: 49px; - bottom: 0px; - width: 100%; - overflow: hidden; - margin: 0; - padding: 0; - border-top: 1px solid var(--pgetinker-content-border-color); - border-bottom: 1px solid var(--pgetinker-content-border-color); -} - -.center { - text-align: center; +.display-flex { + display: flex !important; } .hidden { display: none; } -@import "./console-panel.scss"; -@import "./compiler-output-panel.scss"; -@import "./editor-panel.scss"; -@import "./player-panel.scss"; -@import "./problems-panel.scss"; -@import "./dialog.scss"; - -@import "./responsive.scss"; - .lm_content { overflow: visible !important; } + +@import "./app/dialog.scss"; +@import "./app/mobile-menu-dialog.scss"; +@import "./app/navbar.scss"; +@import "./app/settings-dialog.scss"; +@import "./app/toasts.scss"; +@import "./app/components/CompilerOutputPanel.scss"; +@import "./app/components/ConsolePanel.scss"; +@import "./app/components/EditorPanel.scss"; +@import "./app/components/PlayerPanel.scss"; +@import "./app/components/ProblemsPanel.scss"; +@import "./app/responsive.scss"; diff --git a/resources/css/app/components/CompilerOutputPanel.scss b/resources/css/app/components/CompilerOutputPanel.scss new file mode 100644 index 0000000..90b6856 --- /dev/null +++ b/resources/css/app/components/CompilerOutputPanel.scss @@ -0,0 +1,18 @@ +#compiler-output-panel { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: auto; + + & > div { + font-family: "Droid Sans Mono", "monospace", monospace; + width: 100%; + white-space: pre-wrap; /* Since CSS 2.1 */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ + } +} diff --git a/resources/css/console-panel.scss b/resources/css/app/components/ConsolePanel.scss similarity index 84% rename from resources/css/console-panel.scss rename to resources/css/app/components/ConsolePanel.scss index a15e6d0..091689e 100644 --- a/resources/css/console-panel.scss +++ b/resources/css/app/components/ConsolePanel.scss @@ -6,6 +6,11 @@ bottom: 0; overflow: auto; padding: 1rem 1rem 2rem 1rem; + + & div { + white-space: pre; + font-family: "Droid Sans Mono", "monospace", monospace; + } } #console-auto-scroll { @@ -26,7 +31,3 @@ background-color: var(--pgetinker-link-hover-color); } -#console-panel div { - white-space: pre; - font-family: "Droid Sans Mono", "monospace", monospace; -} diff --git a/resources/css/app/components/EditorPanel.scss b/resources/css/app/components/EditorPanel.scss new file mode 100644 index 0000000..1735052 --- /dev/null +++ b/resources/css/app/components/EditorPanel.scss @@ -0,0 +1,52 @@ +#editor-panel { + position: relative; + width: 100%; + height: 100%; + overflow: visible; + + .code-editor { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 1.2rem; + } + + .status { + position: absolute; + left: 0; + right: 0; + bottom: 0px; + height: 1.2rem; + line-height: 1.2rem; + font-size: 0.8rem; + color: var(--pgetinker-text-color); + border-top: 1px solid var(--pgetinker-content-border-color); + padding-left: 1rem; + padding-right: 1rem; + + .status-left { + float: left; + padding-left: 0.5rem; + + span { + padding-left: 1rem; + } + } + + .status-right { + float: right; + padding-right: 0.5rem; + + span { + padding-right: 1rem; + } + } + + &.too-fucking-big { + background: #f00; + color: #ff0; + font-weight: bolder; + } + } +} diff --git a/resources/css/app/components/PlayerPanel.scss b/resources/css/app/components/PlayerPanel.scss new file mode 100644 index 0000000..a4fbffc --- /dev/null +++ b/resources/css/app/components/PlayerPanel.scss @@ -0,0 +1,62 @@ +#player-panel { + border: none; + display: block; + height: 100%; + width: 100%; + + .compiling { + align-items: center; + display: none; + flex-flow: column; + height: 100%; + justify-content: center; + left: 0; + position: absolute; + top: 0; + width: 100%; + } + + .compiling-failed { + align-items: center; + color: #f00; + display: none; + flex-flow: column; + height: 100%; + justify-content: center; + left: 0; + position: absolute; + top: 0; + width: 100%; + + p { + margin-top: .5rem; + } + + svg { + display: block; + height: 69px; + padding-bottom: 1rem; + width: 69px; + } + } + + .iframe-container { + border: none; + display: block; + height: 100%; + width: 100%; + + iframe { + align-items: center; + border: none; + display: none; + flex-flow: column; + height: 100%; + justify-content: center; + left: 0; + position: absolute; + top: 0; + width: 100%; + } + } +} diff --git a/resources/css/problems-panel.scss b/resources/css/app/components/ProblemsPanel.scss similarity index 100% rename from resources/css/problems-panel.scss rename to resources/css/app/components/ProblemsPanel.scss diff --git a/resources/css/app/dialog.scss b/resources/css/app/dialog.scss new file mode 100644 index 0000000..9b956a2 --- /dev/null +++ b/resources/css/app/dialog.scss @@ -0,0 +1,188 @@ +.dialog +{ + position: absolute; + display: block; + width: 100%; + height: 100%; + left: 0; + top: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.9); + z-index: 9999; + padding: 0; + overflow: hidden; + overflow-y: auto; + + .window { + width: 90vw; + max-width: 80rem; + margin: 10vh auto; + background: var(--pgetinker-dialog-background-color); + font-size: 1.2rem; + color: var(--pgetinker-dialog-text-color); + border: 1px solid var(--pgetinker-dialog-border-color); + border-radius: 0.4rem; + + & > .header { + border-bottom: 1px solid var(--pgetinker-dialog-border-color); + padding: 0.4rem 1rem; + font-weight: bold; + font-size: 2rem; + } + + & > .content { + padding: 1rem; + } + + & > .footer { + border-top: 1px solid var(--pgetinker-dialog-border-color); + padding: 0.4rem 1rem; + text-align: right; + + button { + margin-left: 0.5rem; + border: none; + padding: 0.4rem 1rem; + border-radius: 0.2rem; + + &.ok { + background: #0D6EFD; + color: #fff; + font-weight: bold; + } + + &.ok:hover { + background: #0B5ED7; + color: #fff; + } + + &.cancel { + background: #DC3545; + color: #fff; + font-weight: bold; + } + + &.cancel:hover { + background: #BB2D3B; + color: #fff; + } + } + } + } +} + +.first-time { + .window { + .content { + ul { + padding-left: 1rem; + } + + li { + padding-bottom: 1rem; + } + } + } +} + +.share-dialog { + .window > .content { + img { + display: block; + width: auto; + max-width: 100%; + height: auto; + max-height: 30rem; + margin: 2rem auto; + } + + .input-group { + display: flex; + margin-top: 1rem; + justify-content: center; + + label { + border-radius: 0.4rem 0rem 0rem 0.4rem; + border: 1px solid #000; + background: #4d4d4d; + color: #ffffff; + padding: 0.4rem; + } + + input[type=text] { + border-radius: none; + border: 1px solid #000; + flex-grow: 3; + } + + button { + border-radius: 0rem 0.4rem 0.4rem 0rem; + border: 1px solid #000; + margin: 0; + padding: 0.4rem 1rem; + } + } + } +} + +.news { + .entries { + margin: 1rem 0 3rem 0; + div { + margin-bottom: 1rem; + + &.added:before { + content: "+ Added: "; + color: rgb(34, 167, 172); + } + + &.removed:before { + content: "‐ Removed: "; + color: rgb(248, 102, 102); + } + + &.fixed:before { + content: "✔ Fixed: "; + color: rgb(184, 184, 50); + } + + &.changed:before { + content: "✔ Changed: "; + color: rgb(127, 192, 133); + } + } + } +} + +.supporters { + .window > .content { + a { + display: block; + text-align: center; + margin-top: 2rem; + margin-bottom: 1rem; + font-size: 2.5rem; + } + + h3 { + text-align: center; + font-size: 2rem; + // text-decoration: underline; + padding-bottom: 1rem; + border-bottom: 4px solid var(--pgetinker-dialog-text-color); + } + .names { + display: flex; + width: 100%; + flex-direction: column; + justify-content: center; + align-items: center; + + .name { + padding: .5rem 2rem; + font-size: 2.5rem; + } + } + } +} \ No newline at end of file diff --git a/resources/css/goldenlayout-base.scss b/resources/css/app/goldenlayout-base.scss similarity index 100% rename from resources/css/goldenlayout-base.scss rename to resources/css/app/goldenlayout-base.scss diff --git a/resources/css/goldenlayout-dark-theme.scss b/resources/css/app/goldenlayout-dark-theme.scss similarity index 100% rename from resources/css/goldenlayout-dark-theme.scss rename to resources/css/app/goldenlayout-dark-theme.scss diff --git a/resources/css/goldenlayout-light-theme.scss b/resources/css/app/goldenlayout-light-theme.scss similarity index 100% rename from resources/css/goldenlayout-light-theme.scss rename to resources/css/app/goldenlayout-light-theme.scss diff --git a/resources/css/loading.scss b/resources/css/app/loading.scss similarity index 100% rename from resources/css/loading.scss rename to resources/css/app/loading.scss diff --git a/resources/css/app/mobile-menu-dialog.scss b/resources/css/app/mobile-menu-dialog.scss new file mode 100644 index 0000000..bc95385 --- /dev/null +++ b/resources/css/app/mobile-menu-dialog.scss @@ -0,0 +1,39 @@ +.dialog .menu, +.dialog .submenu { + list-style-type: none; + padding: 0 !important; +} + +.dialog .item, +.dialog .subitem { + padding: .0rem 1rem; +} + +.dialog .has-submenu > a:link, +.dialog .has-submenu > a:active, +.dialog .has-submenu > a:visited, +.dialog .has-submenu > a:hover, +.dialog .has-submenu > a { + border-bottom: 1px solid white; + border-top: 1px solid white; + color: var(--pgetinker-menu-text-color); + display: block; + font-size: 1.6rem; + margin: 1rem 0; + padding: .4rem 1rem; + text-align: center; + text-decoration: none; +} + +.dialog .subitem > a { + background-color: var(--pgetinker-menu-background); + border: 1px solid var(--pgetinker-menu-text-color); + border-radius: .6rem; + color: var(--pgetinker-menu-text-color); + cursor: pointer; + display: block; + font-size: 1.4rem; + margin: 1rem 0; + padding: .6rem 1rem; + text-align: center; +} \ No newline at end of file diff --git a/resources/css/app/navbar.scss b/resources/css/app/navbar.scss new file mode 100644 index 0000000..02e3d89 --- /dev/null +++ b/resources/css/app/navbar.scss @@ -0,0 +1,137 @@ +#header { + nav { + display: flex; + justify-content: space-between; + + .menu { + display: flex; + flex-flow: row; + justify-content:flex-start; + align-items: center; + list-style-type: none; + padding: 0; + margin: 0; + + &.right-menu { + justify-content: flex-end; + } + + .brand { + a:hover, + a:link, + a:active, + a:visited { + display: inline-block; + line-height: 48px; + padding: 0 .8rem; + text-decoration: none; + font-size: 3rem; + color: var(--pgetinker-link-color); + text-shadow: .2rem .2rem .2rem #000; + } + } + + .item { + a, + a:active, + a:link, + a:visited { + background-color: var(--pgetinker-background-color); + border: none; + color: var(--pgetinker-menu-text-color); + cursor: pointer; + display: block; + font-size: 1.2rem; + height: 48px; + letter-spacing: 1px; + line-height: 48px; + padding: 0 .4rem; + text-decoration: none; + text-shadow: .2rem .2rem .5rem var(--pgetinker-text-shadow-color); + } + + a:hover { + background-color: var(--pgetinker-menu-hover-background); + } + + &.has-submenu { + position: relative; + } + + .submenu { + background-color: var(--pgetinker-submenu-background); + border-top: 1px solid var(--pgetinker-content-border-color); + box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); + display: none; + list-style-type: none; + position: absolute; + margin: 0; + padding: 0; + white-space: nowrap; + z-index: 1; + + .subitem { + a { + display: block; + color: var(--pgetinker-submenu-text-color); + font-size: 1.2rem; + text-decoration: none; + text-shadow: .2rem .2rem .5rem var(--pgetinker-text-shadow-color); + padding: .0rem 1rem; + } + a:hover { + background-color: var(--pgetinker-submenu-hover-background); + } + } + } + + &.has-submenu:hover .submenu { + display: block; + } + } + + &.right-menu .has-submenu .submenu { + left: auto; + right: 0; + } + + .separator { + display: none; + flex-grow: 1; + } + } + } + + .lucide { + vertical-align: middle; + filter: drop-shadow(.2rem .2rem .1rem var(--pgetinker-text-shadow-color)); + } +} + +@media screen and (max-width: 750px) { + #header { + nav { + .menu { + &.left-menu { + width: 100%; + } + + #settings-menu { + order: 1; + } + + &.right-menu { + display: none; + } + + .item span { + display: none; + } + + .separator { + display: block; + } + } + } + } +} diff --git a/resources/css/app/responsive.scss b/resources/css/app/responsive.scss new file mode 100644 index 0000000..e69de29 diff --git a/resources/css/app/settings-dialog.scss b/resources/css/app/settings-dialog.scss new file mode 100644 index 0000000..d66d6e6 --- /dev/null +++ b/resources/css/app/settings-dialog.scss @@ -0,0 +1,50 @@ +.settings-dialog { + + .content { + font-family: "Droid Sans Mono", "monospace", monospace; + + button { + width: 100%; + margin: .6rem 0; + padding: .4rem; + } + + select { + width: 100%; + margin: .6rem 0; + padding: .4rem; + } + } + + .form-group { + padding: 1rem; + .form-label { + font-weight: bold; + font-size: 1.4rem; + margin-bottom: .6rem; + } + + .form-description { + padding: .6rem 0; + } + + input[type="checkbox"] { + display: inline-block; + width: 1.6rem; + height: 1.6rem; + } + + label { + display: block; + font-size: 1.2rem; + line-height: 1.6rem; + } + } + + .form-group:hover { + border: 1px solid rgba(0, 0, 255,0.7); + margin: -1px; + } + + +} \ No newline at end of file diff --git a/resources/css/app/toasts.scss b/resources/css/app/toasts.scss new file mode 100644 index 0000000..25b45a4 --- /dev/null +++ b/resources/css/app/toasts.scss @@ -0,0 +1,25 @@ +.toastify { + text-shadow: 1px 1px 0px #000; + font-weight: bold; + &.danger { + background: #bd0000; + color: #fff; + } + + &.warning { + background: #a86326; + color: #fff; + } + + &.info { + background: #02cece; + color: #fff; + + } + + &.success { + background: #01b301; + color: #fff; + } +} + diff --git a/resources/css/compiler-output-panel.scss b/resources/css/compiler-output-panel.scss deleted file mode 100644 index a9e7a81..0000000 --- a/resources/css/compiler-output-panel.scss +++ /dev/null @@ -1,18 +0,0 @@ -#compiler-output-panel { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - overflow: auto; -} - -#compiler-output-panel > div { - font-family: "Droid Sans Mono", "monospace", monospace; - width: 100%; - white-space: pre-wrap; /* Since CSS 2.1 */ - white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ - white-space: -pre-wrap; /* Opera 4-6 */ - white-space: -o-pre-wrap; /* Opera 7 */ - word-wrap: break-word; /* Internet Explorer 5.5+ */ -} diff --git a/resources/css/dialog.scss b/resources/css/dialog.scss deleted file mode 100644 index 93ace2c..0000000 --- a/resources/css/dialog.scss +++ /dev/null @@ -1,171 +0,0 @@ -.dialog -{ - position: absolute; - display: block; - width: 100%; - height: 100%; - left: 0; - top: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.8); - z-index: 9999; - padding: 0; - overflow: hidden; - overflow-y: auto; -} - -.dialog .window { - width: 90vw; - max-width: 80rem; - margin: 10vh auto; - background: var(--pgetinker-dialog-background-color); - font-size: 1.2rem; - color: var(--pgetinker-dialog-text-color); - border: 1px solid var(--pgetinker-dialog-border-color); - border-radius: 0.4rem; -} - -.dialog .window > .header { - border-bottom: 1px solid var(--pgetinker-dialog-border-color); - padding: 0.4rem 1rem; - font-weight: bold; - font-size: 2rem; -} - -.dialog .window > .content { - padding: 1rem; -} - -.dialog .window > .footer { - border-top: 1px solid var(--pgetinker-dialog-border-color); - padding: 0.4rem 1rem; - text-align: right; -} - -.dialog .window .footer button { - margin-left: 0.5rem; - border: none; - padding: 0.4rem 1rem; - border-radius: 0.2rem; -} -.dialog .window > .content img { - display: block; - width: auto; - height: 30rem; - margin: 2rem auto; -} -.ok { - background: #0D6EFD; - color: var(--pgetinker-dialog-text-color); - font-weight: bold; -} - -.ok:hover { - background: #0B5ED7; - color: var(--pgetinker-dialog-text-color); -} - -.cancel { - background: #DC3545; - color: var(--pgetinker-dialog-text-color); - font-weight: bold; -} - -.cancel:hover { - background: #BB2D3B; - color: var(--pgetinker-dialog-text-color); -} - -.input-group { - display: flex; - margin-top: 1rem; - justify-content: center; -} - -.input-group label { - border-radius: 0.4rem 0rem 0rem 0.4rem; - border: 1px solid #000; - background: #4d4d4d; - color: #ffffff; - padding: 0.4rem; -} - -.input-group input[type=text] { - border-radius: none; - border: 1px solid #000; - flex-grow: 3; -} - -.input-group button { - border-radius: 0rem 0.4rem 0.4rem 0rem; - border: 1px solid #000; - margin: 0; - padding: 0.4rem 1rem; -} - -.dialog .window ul { - padding-left: 1rem; -} - -.dialog .window li { - padding-bottom: 1rem; -} - -.dialog.news .entries { - margin: 1rem 0 3rem 0; -} - -.dialog.news .entries div { - margin-bottom: 1rem; -} - -.dialog.news .added:before { - content: "+ Added: "; - color: rgb(92, 250, 255); -} - -.dialog.news .removed:before { - content: "‐ Removed: "; - color: rgb(248, 102, 102); -} - -.dialog.news .fixed:before { - content: "✔ Fixed: "; - color: rgb(252, 252, 170); -} - -.dialog.news .changed:before { - content: "✔ Changed: "; - color: rgb(170, 252, 177); -} - -.dialog.supporters h3 { - text-align: center; - font-size: 2rem; - // text-decoration: underline; - padding-bottom: 1rem; - border-bottom: 4px solid var(--pgetinker-dialog-text-color); -} - -.dialog.supporters .names { - display: flex; - width: 100%; - flex-direction: column; - justify-content: center; - // justify-items: center; - align-items: center; -} - -.dialog.supporters .names .name { - padding: .5rem 2rem; - font-size: 2.5rem; -} - -.dialog.supporters a { - display: block; - text-align: center; - margin-top: 2rem; - margin-bottom: 1rem; - font-size: 2.5rem; -} \ No newline at end of file diff --git a/resources/css/editor-panel.scss b/resources/css/editor-panel.scss deleted file mode 100644 index 8e557f9..0000000 --- a/resources/css/editor-panel.scss +++ /dev/null @@ -1,52 +0,0 @@ -#editor-panel { - position: relative; - width: 100%; - height: 100%; - overflow: visible; -} - -#editor-panel .code-editor { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 1.2rem; -} - -#editor-panel .status { - position: absolute; - left: 0; - right: 0; - bottom: 0px; - height: 1.2rem; - line-height: 1.2rem; - font-size: 0.8rem; - color: var(--pgetinker-text-color); - border-top: 1px solid var(--pgetinker-content-border-color); - padding-left: 1rem; - padding-right: 1rem; -} - -#editor-panel .status-left { - float: left; - padding-left: 0.5rem; -} - -#editor-panel .status-right { - float: right; - padding-right: 0.5rem; -} - -#editor-panel .status-left span { - padding-left: 1rem; -} - -#editor-panel .status-right span { - padding-right: 1rem; -} - -.too-fucking-big { - background: #f00; - color: #ff0; - font-weight: bolder; -} diff --git a/resources/css/light.scss b/resources/css/light.scss deleted file mode 100644 index 7909bf1..0000000 --- a/resources/css/light.scss +++ /dev/null @@ -1,48 +0,0 @@ -body.light { - background: #fff; - color: #000; -} - -body.light a:link, body.light a:visited, body.light a:active { - color: #770; -} - -body.light a:hover { - color: #f70; -} - - -body.light #app, -body.light #player-panel div.loading { - background: #fff; - color: #000; -} - -body.light #info-panel a:active, -body.light #info-panel a:link, -body.light #info-panel a:visited { - color:rgb(196, 99, 99); - text-decoration: none; -} - -body.light .dialog .window { - border: 1px solid #333; - border-radius: 0.4rem; - background: #fff; - color: #000; -} - -body.light .dialog .window > .content { - border-top: 1px solid #333; - border-bottom: 1px solid #333; -} - - -body.light .dialog .window > .content .first-time-dialog h2 { - border-bottom: 2px solid #000; -} - -body.light .dialog .window > .content .first-time-dialog .variable .name { - background: #ddd; -} - diff --git a/resources/css/normalize.scss b/resources/css/normalize.scss new file mode 100644 index 0000000..217b764 --- /dev/null +++ b/resources/css/normalize.scss @@ -0,0 +1 @@ +@import "normalize-css/normalize.css" diff --git a/resources/css/player-panel.scss b/resources/css/player-panel.scss deleted file mode 100644 index 4733223..0000000 --- a/resources/css/player-panel.scss +++ /dev/null @@ -1,71 +0,0 @@ -#player-panel, -#player-panel .iframe-container, -#player-panel .iframe-container iframe { - display: block; - width: 100%; - height: 100%; - border: none; -} -#player-panel .iframe-container iframe, -#player-panel .compiling, -#player-panel .compiling-failed { - display: none; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - justify-content: center; - align-items: center; - flex-flow:column; -} - -#player-panel .compiling-failed svg { - display: block; - width: 69px; - height: 69px; - padding-bottom: 1rem; - -} - -.display-block { - display: block !important; -} - -.display-flex { - display: flex !important; -} - -#player-panel .compiling-failed { - color: #f00; -} - -#player-panel .compiling-failed p { - margin-top: .5rem; -} - - - -// #player-panel div { -// display: none; -// position: absolute; -// top: 0; -// left: 0; -// bottom: 0; -// right: 0; -// } - -// #player-panel div.loading { -// display: block; -// background: #222; -// } - -// #player-panel div.compiling { -// display: block; -// background: #080; -// } - -// #player-panel div.fail { -// display: block; -// background: #800; -// } diff --git a/resources/css/responsive.scss b/resources/css/responsive.scss deleted file mode 100644 index 18ebec7..0000000 --- a/resources/css/responsive.scss +++ /dev/null @@ -1,12 +0,0 @@ -@media screen and (max-width: 1320px) { - nav .navbar-right-menu .sponsor { - display: none; - } -} - -@media screen and (max-width: 750px) { - #settings-menu, - nav .navbar-right-menu { - display: none; - } -} diff --git a/resources/images/screenshot-fail.png b/resources/images/screenshot-fail.png index 9a17bae..ed1c08b 100644 Binary files a/resources/images/screenshot-fail.png and b/resources/images/screenshot-fail.png differ diff --git a/resources/images/screenshot-fail.svg b/resources/images/screenshot-fail.svg index 9430d9a..dfc9fe8 100644 --- a/resources/images/screenshot-fail.svg +++ b/resources/images/screenshot-fail.svg @@ -28,8 +28,8 @@ inkscape:deskcolor="#d1d1d1" inkscape:document-units="px" inkscape:zoom="2.8436027" - inkscape:cx="162.11829" - inkscape:cy="116.04997" + inkscape:cx="163.52495" + inkscape:cy="105.1483" inkscape:window-width="1920" inkscape:window-height="1002" inkscape:window-x="1920" @@ -38,8 +38,8 @@ inkscape:current-layer="layer1" />PGEtinkerCould not make screenshot! + y="29.759184">Could not make screenshot!USECAUTIONUSECAUTION diff --git a/resources/images/technic-skull.svg b/resources/images/technic-skull.svg new file mode 100644 index 0000000..f3b4857 --- /dev/null +++ b/resources/images/technic-skull.svg @@ -0,0 +1,16 @@ + + + + diff --git a/resources/js/app.ts b/resources/js/app.ts index 509f140..ff8d0a2 100644 --- a/resources/js/app.ts +++ b/resources/js/app.ts @@ -1,21 +1,24 @@ import './lib/bootstrap'; +import { conformStorage, getStorageValue, setStorageValue, removeStorageKey } from './lib/storage'; import './lib/goldenLayout'; import './lib/lucide'; import version from "./lib/version"; -import { conformStorage, getStorageValue, setStorageValue, removeStorageKey } from './lib/storage'; // @ts-ignore import agreeDialog from './lib/agreeDialog'; // @ts-ignore -import shareDialog from './lib/shareDialog'; +import mobileMenuDialog from './lib/mobileMenuDialog'; // @ts-ignore import newsDialog from './lib/newsDialog'; // @ts-ignore -import defaultLandscapeLayout from './lib/defaultLandscapeLayout'; +import settingsDialog from './lib/settingsDialog'; // @ts-ignore -import defaultPortraitLayout from './lib/defaultPortraitLayout'; +import shareDialog from './lib/shareDialog'; // @ts-ignore import supportersDialog from './lib/supportersDialog'; - +// @ts-ignore +import defaultLandscapeLayout from './lib/defaultLandscapeLayout'; +// @ts-ignore +import defaultPortraitLayout from './lib/defaultPortraitLayout'; // @ts-ignore import ConsolePanel from './components/ConsolePanel'; // @ts-ignore @@ -26,8 +29,9 @@ import EditorPanel from './components/EditorPanel'; import PlayerPanel from './components/PlayerPanel'; // @ts-ignore import ProblemsPanel from './components/ProblemsPanel'; - import axios from 'axios'; +import { createToast, ToastType } from './lib/createToast'; + declare function GoldenLayout(...args: any[]): void; class PGEtinker @@ -76,117 +80,29 @@ class PGEtinker if(this.theme !== "dark" && this.theme !== "light") this.theme = "dark"; - // Default Code Button - document.querySelector("#default-code")!.addEventListener("click", (event) => + document.querySelector("#settings-menu")?.addEventListener("click", (event) => { event.preventDefault(); - - axios.get("/api/default-code").then((response) => - { - this.editorPanel.setValue(response.data.code); - this.editorPanel.reveal({ - column: 1, - lineNumber: 1, - }); - }).catch((reason) => console.log(reason)); - }); - - // Toggle Theme Button - document.querySelector("#toggle-theme")!.addEventListener("click", (event) => - { - event.preventDefault(); - - if(this.theme === "dark") - this.theme = "light"; - else - this.theme = "dark"; - - this.UpdateTheme(); - }); - - // Default Layout - document.querySelector("#default-layout")!.addEventListener("click", async(event) => - { - event.preventDefault(); - await this.editorPanel.onDestroy(); - - this.layout.destroy(); - - this.layoutConfig = defaultLandscapeLayout; - if(document.body.clientWidth <= 750) { - console.log("chose portrait layout"); - this.layoutConfig = defaultPortraitLayout; + mobileMenuDialog(this); + return; } - - this.SetupLayout(); + settingsDialog(this); }); - + // Download Button - document.querySelector("#download")!.addEventListener("click", (event) => + document.querySelector("#download")?.addEventListener("click", (event) => { event.preventDefault(); - - if(!this.playerPanel.getHtml().includes("Emscripten-Generated Code")) - { - alert("You have to build the code before you can download!") - return; - } - - const a = document.createElement('a'); - - // create the data url - a.href = `data:text/html;base64,${btoa(this.playerPanel.getHtml())}`; - a.download = "pgetinker.html"; - - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); + this.download(); }); // Share Button document.querySelector("#share")!.addEventListener("click", (event) => { event.preventDefault(); - - if(this.compiling) - return; - - if(!this.preCompile()) - return; - - axios.post("/api/share", { - code: this.editorPanel.getValue() - }).then((response) => - { - shareDialog(response.data.shareURL, response.data.shareThumbURL) - .finally(() => - { - this.compileSuccessHandler(response.data); - }); - - }).catch((error) => - { - if(error.response) - { - if(error.response.status) - { - if(error.response.status == 503) - { - this.compileFailHandler("pgetinker.cpp:1:1: error: PGEtinker service has gone offline. try again later.\n"); - return; - } - } - - if(error.response.data.stderr) - { - this.compileFailHandler(error.response.data.stderr); - return; - } - } - this.compileFailHandler("pgetinker.cpp:1:1: error: compilation failed in a way that's not being handled. please make a bug report.\n"); - }); + this.share(); }); // Compile Button @@ -282,6 +198,117 @@ class PGEtinker console.log(`Failed to setActiveTab("${id}")`); } } + + defaultCode() + { + axios.get("/api/default-code").then((response) => + { + this.editorPanel.setValue(response.data.code); + this.editorPanel.reveal({ + column: 1, + lineNumber: 1, + }); + + + }).catch((reason) => console.log(reason)); + } + + download() + { + if(!this.playerPanel.getHtml().includes("Emscripten-Generated Code")) + { + createToast("You have to build the code before you can download!", ToastType.Danger, 10000); + return; + } + + const a = document.createElement('a'); + + // create the data url + a.href = `data:text/html;base64,${btoa(this.playerPanel.getHtml())}`; + a.download = "pgetinker.html"; + + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + createToast("Downloading HTML.", ToastType.Info); + } + + share() + { + let startStopElem = document.querySelector("#start-stop")!; + let playIconElem = startStopElem.querySelector(".lucide-circle-play")!; + let stopIconElem = startStopElem.querySelector(".lucide-circle-stop")!; + let spanElem = startStopElem.querySelector("span")!; + + this.setActiveTab("player"); + + this.consolePanel.setFirstRun(); + playIconElem.classList.toggle("hidden", true); + stopIconElem.classList.toggle("hidden", false); + spanElem.innerHTML = "Stop"; + + if(this.compiling) + return; + + if(!this.preCompile()) + return; + + axios.post("/api/share", { + code: this.editorPanel.getValue() + }).then((response) => + { + shareDialog(response.data.shareURL, response.data.shareThumbURL) + .finally(() => + { + this.compileSuccessHandler(response.data); + }); + + }).catch((error) => + { + this.setActiveTab("editor"); + playIconElem.classList.toggle("hidden", false); + stopIconElem.classList.toggle("hidden", true); + spanElem.innerHTML = "Run"; + + + if(error.response) + { + if(error.response.status) + { + if(error.response.status == 503) + { + this.compileFailHandler("pgetinker.cpp:1:1: error: PGEtinker service has gone offline. try again later.\n"); + return; + } + } + + if(error.response.data.stderr) + { + this.compileFailHandler(error.response.data.stderr); + return; + } + } + + this.compileFailHandler("pgetinker.cpp:1:1: error: compilation failed in a way that's not being handled. please make a bug report.\n"); + }); + } + + async switchToDefaultLayout() + { + await this.editorPanel.onDestroy(); + + this.layout.destroy(); + + this.layoutConfig = defaultLandscapeLayout; + + if(document.body.clientWidth <= 750) + { + console.log("chose portrait layout"); + this.layoutConfig = defaultPortraitLayout; + } + + this.SetupLayout(); + } preCompile() { @@ -348,10 +375,13 @@ class PGEtinker compileSuccessHandler(data: any) { - console.log(data); this.compilerOutputPanel.setContent(data.stdout + data.stderr); this.playerPanel.setHtml(data.html); this.compiling = false; + if(document.body.clientWidth <= 750) + { + createToast("Compile Success.", ToastType.Success); + } } compileFailHandler(stderr: any) @@ -361,8 +391,11 @@ class PGEtinker this.compilerOutputPanel.setContent(stderr); this.playerPanel.setCompilingFailed(); this.compiling = false; + if(document.body.clientWidth <= 750) + { + createToast("Compile Failed.", ToastType.Danger); + } } - async SetupLayout() { @@ -431,7 +464,7 @@ class PGEtinker let light = (this.theme === "light"); // update editor theme - await this.editorPanel.setTheme(this.theme); + await this.editorPanel.updateConfiguration(); setTimeout(() => { @@ -453,7 +486,3 @@ class PGEtinker } new PGEtinker(); - - - - diff --git a/resources/js/components/EditorPanel.js b/resources/js/components/EditorPanel.js index eac23a0..020c437 100644 --- a/resources/js/components/EditorPanel.js +++ b/resources/js/components/EditorPanel.js @@ -206,7 +206,7 @@ export default class EditorPanel this.monacoWrapper.getEditor().revealPositionInCenter(position); } - async setTheme(theme) + async updateConfiguration() { if(this.monacoWrapper == null) return; @@ -214,7 +214,7 @@ export default class EditorPanel await this.monacoWrapper .getMonacoEditorApp() - .updateUserConfiguration(getUserConfiguration(theme)); + .updateUserConfiguration(getUserConfiguration()); } updateStatusBar() diff --git a/resources/js/lib/agreeDialog.js b/resources/js/lib/agreeDialog.js index de245f4..fea0305 100644 --- a/resources/js/lib/agreeDialog.js +++ b/resources/js/lib/agreeDialog.js @@ -2,12 +2,12 @@ export default function agreeDialog() { return new Promise((resolve, reject) => { - let agreeDialog = document.createElement("div"); + let dialog = document.createElement("div"); - agreeDialog.classList.toggle("dialog", true); - agreeDialog.classList.toggle("first-time", true); + dialog.classList.toggle("dialog", true); + dialog.classList.toggle("first-time", true); - agreeDialog.innerHTML = ` + dialog.innerHTML = `
Welome to PGEtinker!
@@ -56,19 +56,19 @@ export default function agreeDialog()
`; - agreeDialog.querySelector("#i-disagree").addEventListener("click", (event) => + dialog.querySelector("#i-disagree").addEventListener("click", (event) => { reject(); - agreeDialog.remove(); + dialog.remove(); }); - agreeDialog.querySelector("#i-agree").addEventListener("click", (event) => + dialog.querySelector("#i-agree").addEventListener("click", (event) => { resolve(); - agreeDialog.remove(); + dialog.remove(); }); - document.body.appendChild(agreeDialog); + document.body.appendChild(dialog); }); } \ No newline at end of file diff --git a/resources/js/lib/createToast.ts b/resources/js/lib/createToast.ts new file mode 100644 index 0000000..ee7ef8b --- /dev/null +++ b/resources/js/lib/createToast.ts @@ -0,0 +1,35 @@ +// @ts-ignore +import Toastify from "toastify-js"; + + +export const ToastType = { + Danger: "danger", + Info: "info", + Warn: "warning", + Success: "success", +}; + +export function createToast(message: string, type: string, duration? : number) +{ + duration = (duration) ? duration : 3000; + + if(message == null) + throw Error("message not set"); + + if(type == null) + throw Error("type not set"); + + let gravity = "top"; + + if(document.body.clientWidth <= 750) + gravity = "bottom"; + + Toastify({ + text: message, + className: type, + position: "center", + gravity, + duration + }).showToast(); +} + diff --git a/resources/js/lib/lucide.js b/resources/js/lib/lucide.js index a0a6820..aa1d29b 100644 --- a/resources/js/lib/lucide.js +++ b/resources/js/lib/lucide.js @@ -14,7 +14,8 @@ import { Share2, SquareChevronRight, SunMoon, - UndoDot, } from 'lucide'; + UndoDot, + Youtube } from 'lucide'; createIcons({ icons: { @@ -32,6 +33,7 @@ createIcons({ Share2, SquareChevronRight, SunMoon, - UndoDot + UndoDot, + Youtube }, }); \ No newline at end of file diff --git a/resources/js/lib/mobileMenuDialog.js b/resources/js/lib/mobileMenuDialog.js new file mode 100644 index 0000000..c01e821 --- /dev/null +++ b/resources/js/lib/mobileMenuDialog.js @@ -0,0 +1,208 @@ +import settingsDialog from "./settingsDialog"; +import newsDialog from "./newsDialog"; +import supportersDialog from "./supportersDialog"; + +export default function mobileMenuDialog(state) +{ + return new Promise((resolve) => + { + let dialog = document.createElement('div'); + + dialog.classList.toggle("dialog", "true"); + dialog.classList.toggle("mobile-menu-dialog", "true"); + + dialog.innerHTML = ` +
+
Menu
+ + +
`; + + dialog.querySelector("#settings-menu").addEventListener("click", (event) => + { + event.preventDefault(); + settingsDialog(state).then(() => + { + dialog.remove(); + resolve() + }); + }); + + dialog.querySelector("#news-and-updates").addEventListener("click", (event) => + { + event.preventDefault(); + newsDialog().then(() => + { + dialog.remove(); + resolve() + }); + }); + + dialog.querySelector("#supporters").addEventListener("click", (event) => + { + event.preventDefault(); + supportersDialog().then(() => + { + dialog.remove(); + resolve(); + }); + }); + + dialog.querySelector("#download").addEventListener("click", (event) => + { + event.preventDefault(); + state.download(); + dialog.remove(); + resolve(); + }); + + dialog.querySelector("#share").addEventListener("click", (event) => + { + event.preventDefault(); + state.share(); + dialog.remove(); + resolve(); + }); + + dialog.querySelector(".ok").addEventListener("click", (event) => + { + dialog.remove(); + resolve(); + }); + + document.body.appendChild(dialog); + }); +} + + + // Default Code Button + // document.querySelector("#default-code")!.addEventListener("click", (event) => + // { + // event.preventDefault(); + + // axios.get("/api/default-code").then((response) => + // { + // this.editorPanel.setValue(response.data.code); + // this.editorPanel.reveal({ + // column: 1, + // lineNumber: 1, + // }); + // }).catch((reason) => console.log(reason)); + // }); + + // Toggle Theme Button + // document.querySelector("#toggle-theme")!.addEventListener("click", (event) => + // { + // event.preventDefault(); + + // if(this.theme === "dark") + // this.theme = "light"; + // else + // this.theme = "dark"; + + // this.UpdateTheme(); + // }); + + // Default Layout + // document.querySelector("#default-layout")!.addEventListener("click", async(event) => + // { + // event.preventDefault(); + // await this.editorPanel.onDestroy(); + + // this.layout.destroy(); + + // this.layoutConfig = defaultLandscapeLayout; + + // if(document.body.clientWidth <= 750) + // { + // console.log("chose portrait layout"); + // this.layoutConfig = defaultPortraitLayout; + // } + + // this.SetupLayout(); + // }); \ No newline at end of file diff --git a/resources/js/lib/monacoConfig.js b/resources/js/lib/monacoConfig.js index 1db5906..7dcc441 100644 --- a/resources/js/lib/monacoConfig.js +++ b/resources/js/lib/monacoConfig.js @@ -8,17 +8,20 @@ import getEditorServiceOverride from '@codingame/monaco-vscode-editor-service-ov import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings-service-override' import monacoVscodeTextmateServiceOverride from '@codingame/monaco-vscode-textmate-service-override'; import { useOpenEditorStub } from 'monaco-editor-wrapper/vscode/services'; +import { getStorageValue, setStorageValue } from './storage'; -export const getUserConfiguration = (theme) => +export const getUserConfiguration = () => { - theme = (theme === undefined) ? "dark" : theme; return JSON.stringify({ - "workbench.colorTheme": (theme == "dark") ? "Default Dark Modern" : "Default Light Modern", + "workbench.colorTheme": (getStorageValue("theme") == "dark") ? "Default Dark Modern" : "Default Light Modern", "editor.mouseWheelZoom": "on", "editor.wordBasedSuggestions": "off", "editor.quickSuggestionDelay": 500, - "editor.inlayHints.enabled": "off", + "editor.inlayHints.enabled": getStorageValue("editor.inlayHints.enabled"), + "editor.tabSize": 4, + "editor.indentSize": 4, + "editor.detectIndentation": false, }); } @@ -59,11 +62,27 @@ export const createUserConfig = (workspaceRoot, code, codeUri) => if(uri.path != "/workspace/pgetinker.cpp") return next(uri, diagnostics); - window.dispatchEvent(new CustomEvent("update-problems-panel", { detail: diagnostics })); - return next(uri, diagnostics); + let filteredDiagnostics = []; + + diagnostics.forEach((diagnostic) => + { + // Javid Mode? + if(getStorageValue("diagnostics.javidMode")) + { + if(diagnostic.source === "clang-tidy") + { + return; + } + } + + filteredDiagnostics.push(diagnostic); + }); + + + window.dispatchEvent(new CustomEvent("update-problems-panel", { detail: filteredDiagnostics })); + return next(uri, filteredDiagnostics); }, }, - connectionOptions: { maxRestartCount: 5, } diff --git a/resources/js/lib/newsDialog.js b/resources/js/lib/newsDialog.js index 115d0c2..d736def 100644 --- a/resources/js/lib/newsDialog.js +++ b/resources/js/lib/newsDialog.js @@ -1,24 +1,12 @@ import version from "./version"; export default function newsDialog() { - function newsClickAnywhereHandler(event) - { - let newsDialog = document.querySelector(".dialog.news"); - if(newsDialog == null) - return; - - if(event.target.tagName == 'A') - return; - - newsDialog.dispatchEvent(new Event("close-dialog")); - } - return new Promise((resolve) => { - let newsDialog = document.createElement("div"); + let dialog = document.createElement("div"); - newsDialog.classList.toggle("dialog", true); - newsDialog.classList.toggle("news", true); + dialog.classList.toggle("dialog", true); + dialog.classList.toggle("news", true); axios.get("/api/news").then((response) => { @@ -30,7 +18,7 @@ export default function newsDialog() entries.push(`
${entry.message}
`); }); - newsDialog.innerHTML = ` + dialog.innerHTML = `
News and Updates
@@ -45,17 +33,18 @@ export default function newsDialog() Version: ${version.substring(0, 7)}

+
`; - newsDialog.addEventListener("close-dialog", (event) => + dialog.querySelector("button.ok").addEventListener("click", (event) => { - setTimeout(() => window.removeEventListener("click", newsClickAnywhereHandler), 500); - newsDialog.remove(); + dialog.remove(); resolve(); }); - setTimeout(() => window.addEventListener("click", newsClickAnywhereHandler), 500); - document.body.appendChild(newsDialog); + document.body.appendChild(dialog); }); }); diff --git a/resources/js/lib/settingsDialog.js b/resources/js/lib/settingsDialog.js new file mode 100644 index 0000000..68cef80 --- /dev/null +++ b/resources/js/lib/settingsDialog.js @@ -0,0 +1,217 @@ +import { createToast, ToastType } from './createToast'; +import { getStorageValue, setStorageValue } from './storage'; + +let fieldId = 0; + +/** + * + * @param {string} label + * @param {string} description + * @param {(event) => void} valueCallback + * @param {options[]} options + * @param {string} initialValue + * @returns + */ +function select(label, description, valueCallback, options, initialValue) +{ + fieldId++; + let select = document.createElement("div") + + select.classList.toggle("form-group", true); + select.classList.toggle("select", true); + + select.innerHTML = ` +
${label}
+
${description}
+ + `; + + for(let i = 0; i < options.length; i++) + { + let option = document.createElement("option"); + option.value = options[i]; + option.innerHTML = options[i]; + + if(options[i] == initialValue) + { + option.selected = true; + } + + select.querySelector("select").append(option); + } + + select.addEventListener("change", (event) => + { + valueCallback(event); + }); + + return select; +} + +/** + * + * @param {string} label + * @param {string} description + * @param {(event) => void} valueCallback + * @param {boolean} initialValue + * @returns + */ +function toggle(label, description, valueCallback, initialValue) +{ + fieldId++; + let toggle = document.createElement("div") + + toggle.classList.toggle("form-group", true); + toggle.classList.toggle("toggle", true); + + toggle.innerHTML = ` +
${label}
+ + `; + + toggle.addEventListener("change", (event) => + { + event.preventDefault(); + valueCallback(event); + }); + + return toggle; +} + +/** + * + * @param {string} label + * @param {(event) => void} callback + * @returns + */ +function button(label, callback) +{ + fieldId++; + let button = document.createElement("button"); + button.setAttribute("name", `button-${fieldId}`); + + button.innerHTML = label; + + button.addEventListener("click", (event) => + { + callback(event); + }); + + return button; +} + +export default function settingsDialog(state) +{ + + return new Promise((resolve) => + { + let dialog = document.createElement('div'); + + dialog.classList.toggle("dialog", "true"); + dialog.classList.toggle("settings-dialog", "true"); + + dialog.innerHTML = ` +
+
Settings
+
+
+ +
`; + + + dialog.querySelector(".content").append(button( + "Load Default Code", + (event) => + { + state.defaultCode(); + createToast("Loaded default code.", ToastType.Info); + } + )); + + dialog.querySelector(".content").append(button( + "Restore Default Layout", + async(event) => + { + await state.switchToDefaultLayout(); + createToast("Restored default layout.", ToastType.Info); + } + )); + + dialog.querySelector(".content").append(select( + "Theme", + "Choose from the available themes!", + (event) => + { + if(event.target.value === "dark") + state.theme = "dark"; + + if(event.target.value === "light") + state.theme = "light"; + + createToast(`Changing theme: ${state.theme}`, ToastType.Info); + state.UpdateTheme(); + }, + ["dark", "light"], + state.theme + )); + + + dialog.querySelector(".content").append(toggle( + "Editor> Inlay Hints: Enabled", + "Enables the inlay hints in the editor.", + (event) => + { + setStorageValue("editor.inlayHints.enabled", event.target.checked); + state.editorPanel.updateConfiguration(); + }, + getStorageValue("editor.inlayHints.enabled") + )); + + + dialog.querySelector(".content").append(toggle( + "Diagnostics> Javid Mode: Enabled", + "Enabling Javid Mode prevents Clang Tidy warnings from appearing in the editor and the problems panel.", + (event) => + { + setStorageValue("diagnostics.javidMode", event.target.checked); + if(event.target.checked) + { + window.dispatchEvent(new CustomEvent("update-problems-panel", { detail: [] })); + } + }, + getStorageValue("diagnostics.javidMode") + )); + + + // dialog.querySelector(".content").append(toggle( + // "The Label", + // "The Description", + // (event) => + // { + // console.log(event.target.checked, "something happened 3"); + // } + // )); + + // dialog.querySelector(".content").append(toggle( + // "The Label", + // "The Description", + // (event) => + // { + // console.log(event.target.checked, "something happened 4"); + // } + // )); + + dialog.querySelector(".ok").addEventListener("click", (event) => + { + dialog.remove(); + resolve(); + }); + + document.body.appendChild(dialog); + }); +} diff --git a/resources/js/lib/shareDialog.js b/resources/js/lib/shareDialog.js index c946a72..f095a63 100644 --- a/resources/js/lib/shareDialog.js +++ b/resources/js/lib/shareDialog.js @@ -1,21 +1,16 @@ +import { createToast, ToastType } from './createToast'; + export default function shareDialog(shareUrl, shareThumbUrl) { - function shareClickAnywhereHandler(event) - { - let shareDialog = document.querySelector(".share-dialog"); - if(shareDialog == null) - return; - - shareDialog.querySelector("button").dispatchEvent(new Event("click")); - } - + let copied = false; + return new Promise((resolve) => { - let shareDialog = document.createElement('div'); + let dialog = document.createElement('div'); - shareDialog.classList.toggle("dialog", "true"); - shareDialog.classList.toggle("share-dialog", "true"); - shareDialog.innerHTML = ` + dialog.classList.toggle("dialog", "true"); + dialog.classList.toggle("share-dialog", "true"); + dialog.innerHTML = `
Share Your Masterpiece!
@@ -23,22 +18,33 @@ export default function shareDialog(shareUrl, shareThumbUrl)
- +
+
`; - shareDialog.querySelector("button").addEventListener("click", (event) => + dialog.querySelector("button.copy-url").addEventListener("click", (event) => { navigator.clipboard.writeText(shareUrl).catch((reason) => console.log(reason)); - shareDialog.remove(); - window.removeEventListener("click", shareClickAnywhereHandler); + createToast("Copied URL to clipboard.", ToastType.Info); + copied = true; + }); + + dialog.querySelector("button.ok").addEventListener("click", (event) => + { + if(!copied) + { + createToast("Copied URL to clipboard.", ToastType.Info); + navigator.clipboard.writeText(shareUrl).catch((reason) => console.log(reason)); + } + + dialog.remove(); resolve(); }); - document.body.appendChild(shareDialog); - - // you're welcome dandistine - window.addEventListener("click", shareClickAnywhereHandler); + document.body.appendChild(dialog); }); } diff --git a/resources/js/lib/storage.ts b/resources/js/lib/storage.ts index 32e6a03..f17313f 100644 --- a/resources/js/lib/storage.ts +++ b/resources/js/lib/storage.ts @@ -23,6 +23,16 @@ export function conformStorage(): void { setStorageValue("theme", theme); } + + if(getStorageValue("diagnostics.javidMode") == null) + { + setStorageValue("diagnostics.javidMode", false); + } + + if(getStorageValue("editor.inlayHints.enabled") == null) + { + setStorageValue("editor.inlayHints.enabled", false); + } } export function getStorageValue(key: string): string | null diff --git a/resources/js/lib/supportersDialog.js b/resources/js/lib/supportersDialog.js index 06d328d..a01927c 100644 --- a/resources/js/lib/supportersDialog.js +++ b/resources/js/lib/supportersDialog.js @@ -1,75 +1,66 @@ export default function supportersDialog() { - function supportersClickAnywhereHandler(event) - { - let supportersDialog = document.querySelector(".dialog.supporters"); - if(supportersDialog == null) - return; - - if(event.target.tagName == 'A') - return; - - supportersDialog.dispatchEvent(new Event("close-dialog")); - } - function renderSupportersDialog(supporters) { - let supportersDialog = document.createElement("div"); - - supportersDialog.classList.toggle("dialog", true); - supportersDialog.classList.toggle("supporters", true); - - let entries = []; - - if(supporters.length > 0) + return new Promise((resolve) => { - // sort biggest first - supporters.sort((a, b) => b.amount - a.amount); - - supporters.forEach((entry) => + let dialog = document.createElement("div"); + + dialog.classList.toggle("dialog", true); + dialog.classList.toggle("supporters", true); + + let entries = []; + + if(supporters.length > 0) { - entries.push(`
◀ ${entry.name} ▶
`); - }); - } - else - { - entries.push(`
◀ No Supporters Yet ▶
`); - } + // sort biggest first + supporters.sort((a, b) => b.amount - a.amount); + + supporters.forEach((entry) => + { + entries.push(`
◀ ${entry.name} ▶
`); + }); + } + else + { + entries.push(`
◀ No Supporters Yet ▶
`); + } - supportersDialog.innerHTML = ` -
-
Patreon Supporters!
-
-

PGEtinker would not exist without the support of:

-
- ${entries.join('')} + dialog.innerHTML = ` +
+
Patreon Supporters!
+
+

PGEtinker would not exist without the support of:

+
+ ${entries.join('')} +
+ + Become a Supporter + +
+ - - Become a Supporter - -
-
`; +
`; - supportersDialog.addEventListener("close-dialog", (event) => - { - setTimeout(() => window.removeEventListener("click", supportersClickAnywhereHandler), 500); - supportersDialog.remove(); + dialog.querySelector("button.ok").addEventListener("click", (event) => + { + dialog.remove(); + resolve(); + }); + + document.body.appendChild(dialog); }); - - setTimeout(() => window.addEventListener("click", supportersClickAnywhereHandler), 500); - document.body.appendChild(supportersDialog); } - return new Promise(() => + return new Promise((resolve) => { axios.get("/api/supporters").then((response) => { - renderSupportersDialog(response.data.supporters); + renderSupportersDialog(response.data.supporters).then(() => resolve()); }).catch((reason) => { - renderSupportersDialog(reason.response.data.supporters); + renderSupportersDialog(reason.response.data.supporters).then(() => resolve()); }); - }); - -} \ No newline at end of file +} diff --git a/resources/views/disagree.blade.php b/resources/views/disagree.blade.php index 59d91d6..ad883dc 100644 --- a/resources/views/disagree.blade.php +++ b/resources/views/disagree.blade.php @@ -6,6 +6,7 @@ You disagreed | PGEtinker + @vite("resources/js/disagreed.js") diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 49dfffd..82fb72f 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -30,108 +30,129 @@ PGEtinker - - - + + + + + @vite('resources/js/app.ts')