From aba163e52b4498a4f3771784af7e666c5ef94fca Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Wed, 10 May 2023 00:11:26 -0500 Subject: [PATCH 01/20] feat: Hot Reloading --- app/Config/Events.php | 6 + app/Config/Toolbar.php | 27 + system/Debug/Toolbar/Views/toolbar.css | 880 ++++++++++---------- system/Debug/Toolbar/Views/toolbar.js | 799 ++++++++++-------- system/Debug/Toolbar/Views/toolbar.tpl.php | 5 + system/HotReloader/DirectoryHasher.php | 61 ++ system/HotReloader/HotReloader.php | 52 ++ system/HotReloader/IteratorFilter.php | 39 + user_guide_src/source/changelogs/v4.4.0.rst | 2 + user_guide_src/source/testing/debugging.rst | 11 + 10 files changed, 1098 insertions(+), 784 deletions(-) create mode 100644 system/HotReloader/DirectoryHasher.php create mode 100644 system/HotReloader/HotReloader.php create mode 100644 system/HotReloader/IteratorFilter.php diff --git a/app/Config/Events.php b/app/Config/Events.php index 5219f4ac3f68..bb48c66efde8 100644 --- a/app/Config/Events.php +++ b/app/Config/Events.php @@ -44,5 +44,11 @@ if (CI_DEBUG && ! is_cli()) { Events::on('DBQuery', 'CodeIgniter\Debug\Toolbar\Collectors\Database::collect'); Services::toolbar()->respond(); + // Hot Reload route - for framework use on the hot reloader. + if (ENVIRONMENT === 'development') { + Services::routes()->get('__hot-reload', function() { + (new \CodeIgniter\HotReloader\HotReloader())->run(); + }); + } } }); diff --git a/app/Config/Toolbar.php b/app/Config/Toolbar.php index ecab7a2cc1d3..38a42b59f1cf 100644 --- a/app/Config/Toolbar.php +++ b/app/Config/Toolbar.php @@ -88,4 +88,31 @@ class Toolbar extends BaseConfig * `$maxQueries` defines the maximum amount of queries that will be stored. */ public int $maxQueries = 100; + + /** + * -------------------------------------------------------------------------- + * Watched Directories + * -------------------------------------------------------------------------- + * + * Contains an array of directories that will be watched for changes and + * used to determine if the hot-reload feature should reload the page or not. + * We restrict the values to keep performance as high as possible. + * + * NOTE: The ROOTPATH will be prepended to all values. + */ + public array $watchedDirectories = [ + 'app', + ]; + + /** + * -------------------------------------------------------------------------- + * Watched File Extensions + * -------------------------------------------------------------------------- + * + * Contains an array of file extensions that will be watched for changes and + * used to determine if the hot-reload feature should reload the page or not. + */ + public array $watchedExtensions = [ + 'php', 'css', 'js', 'html', 'svg', 'json', 'env' + ]; } diff --git a/system/Debug/Toolbar/Views/toolbar.css b/system/Debug/Toolbar/Views/toolbar.css index 744d9392c2be..b8954dacfde3 100644 --- a/system/Debug/Toolbar/Views/toolbar.css +++ b/system/Debug/Toolbar/Views/toolbar.css @@ -7,321 +7,327 @@ * file that was distributed with this source code. */ #debug-icon { - bottom: 0; - position: fixed; - right: 0; - z-index: 10000; - height: 36px; - width: 36px; - margin: 0px; - padding: 0px; - clear: both; - text-align: center; + bottom: 0; + position: fixed; + right: 0; + z-index: 10000; + height: 36px; + width: 36px; + margin: 0px; + padding: 0px; + clear: both; + text-align: center; } #debug-icon a svg { - margin: 8px; - max-width: 20px; - max-height: 20px; + margin: 8px; + max-width: 20px; + max-height: 20px; } #debug-icon.fixed-top { - bottom: auto; - top: 0; + bottom: auto; + top: 0; } #debug-icon .debug-bar-ndisplay { - display: none; + display: none; } #debug-bar { - bottom: 0; - left: 0; - position: fixed; - right: 0; - z-index: 10000; - height: 36px; - line-height: 36px; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; - font-size: 16px; - font-weight: 400; + bottom: 0; + left: 0; + position: fixed; + right: 0; + z-index: 10000; + height: 36px; + line-height: 36px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, + sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + font-size: 16px; + font-weight: 400; } #debug-bar h1 { - display: flex; - font-weight: normal; - margin: 0 0 0 auto; + display: flex; + font-weight: normal; + margin: 0 0 0 auto; } #debug-bar h1 svg { - width: 16px; - margin-right: 5px; + width: 16px; + margin-right: 5px; } #debug-bar h2 { - font-size: 16px; - margin: 0; - padding: 5px 0 10px 0; + font-size: 16px; + margin: 0; + padding: 5px 0 10px 0; } #debug-bar h2 span { - font-size: 13px; + font-size: 13px; } #debug-bar h3 { - font-size: 12px; - font-weight: 200; - margin: 0 0 0 10px; - padding: 0; - text-transform: uppercase; + font-size: 12px; + font-weight: 200; + margin: 0 0 0 10px; + padding: 0; + text-transform: uppercase; } #debug-bar p { - font-size: 12px; - margin: 0 0 0 15px; - padding: 0; + font-size: 12px; + margin: 0 0 0 15px; + padding: 0; } #debug-bar a { - text-decoration: none; + text-decoration: none; } #debug-bar a:hover { - text-decoration: underline; + text-decoration: underline; } #debug-bar button { - border: 1px solid; - border-radius: 4px; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - cursor: pointer; - line-height: 15px; + border: 1px solid; + border-radius: 4px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + cursor: pointer; + line-height: 15px; } #debug-bar button:hover { - text-decoration: underline; + text-decoration: underline; } #debug-bar table { - border-collapse: collapse; - font-size: 14px; - line-height: normal; - margin: 5px 10px 15px 10px; - width: calc(100% - 10px); + border-collapse: collapse; + font-size: 14px; + line-height: normal; + margin: 5px 10px 15px 10px; + width: calc(100% - 10px); } #debug-bar table strong { - font-weight: 500; + font-weight: 500; } #debug-bar table th { - display: table-cell; - font-weight: 600; - padding-bottom: 0.7em; - text-align: left; + display: table-cell; + font-weight: 600; + padding-bottom: 0.7em; + text-align: left; } #debug-bar table tr { - border: none; + border: none; } #debug-bar table td { - border: none; - display: table-cell; - margin: 0; - text-align: left; + border: none; + display: table-cell; + margin: 0; + text-align: left; } #debug-bar table td:first-child { - max-width: 20%; + max-width: 20%; } #debug-bar table td:first-child.narrow { - width: 7em; + width: 7em; } #debug-bar td[data-debugbar-route] form { - display: none; + display: none; } #debug-bar td[data-debugbar-route]:hover form { - display: block; + display: block; } #debug-bar td[data-debugbar-route]:hover > div { - display: none; + display: none; } -#debug-bar td[data-debugbar-route] input[type=text] { - padding: 2px; +#debug-bar td[data-debugbar-route] input[type="text"] { + padding: 2px; } #debug-bar .toolbar { - display: flex; - overflow: hidden; - overflow-y: auto; - padding: 0 12px 0 12px; - white-space: nowrap; - z-index: 10000; + display: flex; + overflow: hidden; + overflow-y: auto; + padding: 0 12px 0 12px; + white-space: nowrap; + z-index: 10000; } #debug-bar.fixed-top { - bottom: auto; - top: 0; + bottom: auto; + top: 0; } #debug-bar.fixed-top .tab { - bottom: auto; - top: 36px; + bottom: auto; + top: 36px; } #debug-bar #toolbar-position a, #debug-bar #toolbar-theme a { - padding: 0 6px; - display: inline-flex; - vertical-align: top; + padding: 0 6px; + display: inline-flex; + vertical-align: top; } #debug-bar #toolbar-position a:hover, #debug-bar #toolbar-theme a:hover { - text-decoration: none; + text-decoration: none; } #debug-bar #debug-bar-link { - display: flex; - padding: 6px; + display: flex; + padding: 6px; } #debug-bar .ci-label { - display: inline-flex; - font-size: 14px; + display: inline-flex; + font-size: 14px; } #debug-bar .ci-label:hover { - cursor: pointer; + cursor: pointer; } #debug-bar .ci-label a { - color: inherit; - display: flex; - letter-spacing: normal; - padding: 0 10px; - text-decoration: none; - align-items: center; + color: inherit; + display: flex; + letter-spacing: normal; + padding: 0 10px; + text-decoration: none; + align-items: center; } #debug-bar .ci-label img { - margin: 6px 3px 6px 0; - width: 16px !important; + margin: 6px 3px 6px 0; + width: 16px !important; } #debug-bar .ci-label .badge { - border-radius: 12px; - -moz-border-radius: 12px; - -webkit-border-radius: 12px; - display: inline-block; - font-size: 75%; - font-weight: bold; - line-height: 12px; - margin-left: 5px; - padding: 2px 5px; - text-align: center; - vertical-align: baseline; - white-space: nowrap; + border-radius: 12px; + -moz-border-radius: 12px; + -webkit-border-radius: 12px; + display: inline-block; + font-size: 75%; + font-weight: bold; + line-height: 12px; + margin-left: 5px; + padding: 2px 5px; + text-align: center; + vertical-align: baseline; + white-space: nowrap; } #debug-bar .tab { - bottom: 35px; - display: none; - left: 0; - max-height: 62%; - overflow: hidden; - overflow-y: auto; - padding: 1em 2em; - position: fixed; - right: 0; - z-index: 9999; + bottom: 35px; + display: none; + left: 0; + max-height: 62%; + overflow: hidden; + overflow-y: auto; + padding: 1em 2em; + position: fixed; + right: 0; + z-index: 9999; } #debug-bar .timeline { - margin-left: 0; - width: 100%; + margin-left: 0; + width: 100%; } #debug-bar .timeline th { - border-left: 1px solid; - font-size: 12px; - font-weight: 200; - padding: 5px 5px 10px 5px; - position: relative; - text-align: left; + border-left: 1px solid; + font-size: 12px; + font-weight: 200; + padding: 5px 5px 10px 5px; + position: relative; + text-align: left; } #debug-bar .timeline th:first-child { - border-left: 0; + border-left: 0; } #debug-bar .timeline td { - border-left: 1px solid; - padding: 5px; - position: relative; + border-left: 1px solid; + padding: 5px; + position: relative; } #debug-bar .timeline td:first-child { - border-left: 0; - max-width: none; + border-left: 0; + max-width: none; } #debug-bar .timeline td.child-container { - padding: 0px; + padding: 0px; } #debug-bar .timeline td.child-container .timeline { - margin: 0px; + margin: 0px; } -#debug-bar .timeline td.child-container .timeline td:first-child:not(.child-container) { - padding-left: calc(5px + 10px * var(--level)); +#debug-bar + .timeline + td.child-container + .timeline + td:first-child:not(.child-container) { + padding-left: calc(5px + 10px * var(--level)); } #debug-bar .timeline .timer { - border-radius: 4px; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - display: inline-block; - padding: 5px; - position: absolute; - top: 30%; + border-radius: 4px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + display: inline-block; + padding: 5px; + position: absolute; + top: 30%; } #debug-bar .timeline .timeline-parent { - cursor: pointer; + cursor: pointer; } #debug-bar .timeline .timeline-parent td:first-child nav { - background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMCAxNTAiPjxwYXRoIGQ9Ik02IDdoMThsLTkgMTV6bTAgMzBoMThsLTkgMTV6bTAgNDVoMThsLTktMTV6bTAgMzBoMThsLTktMTV6bTAgMTJsMTggMThtLTE4IDBsMTgtMTgiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNNiAxMjZsMTggMThtLTE4IDBsMTgtMTgiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSIjNTU1Ii8+PC9zdmc+") no-repeat scroll 0 0/15px 75px transparent; - background-position: 0 25%; - display: inline-block; - height: 15px; - width: 15px; - margin-right: 3px; - vertical-align: middle; + background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMCAxNTAiPjxwYXRoIGQ9Ik02IDdoMThsLTkgMTV6bTAgMzBoMThsLTkgMTV6bTAgNDVoMThsLTktMTV6bTAgMzBoMThsLTktMTV6bTAgMTJsMTggMThtLTE4IDBsMTgtMTgiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNNiAxMjZsMTggMThtLTE4IDBsMTgtMTgiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSIjNTU1Ii8+PC9zdmc+") + no-repeat scroll 0 0/15px 75px transparent; + background-position: 0 25%; + display: inline-block; + height: 15px; + width: 15px; + margin-right: 3px; + vertical-align: middle; } #debug-bar .timeline .timeline-parent-open { - background-color: #DFDFDF; + background-color: #dfdfdf; } #debug-bar .timeline .timeline-parent-open td:first-child nav { - background-position: 0 75%; + background-position: 0 75%; } #debug-bar .timeline .child-row:hover { - background: transparent; + background: transparent; } #debug-bar .route-params, #debug-bar .route-params-item { - vertical-align: top; + vertical-align: top; } #debug-bar .route-params td:first-child, #debug-bar .route-params-item td:first-child { - font-style: italic; - padding-left: 1em; - text-align: right; + font-style: italic; + padding-left: 1em; + text-align: right; } .debug-view.show-view { - border: 1px solid; - margin: 4px; + border: 1px solid; + margin: 4px; } .debug-view-path { - font-family: monospace; - font-size: 12px; - letter-spacing: normal; - min-height: 16px; - padding: 2px; - text-align: left; + font-family: monospace; + font-size: 12px; + letter-spacing: normal; + min-height: 16px; + padding: 2px; + text-align: left; } .show-view .debug-view-path { - display: block !important; + display: block !important; } @media screen and (max-width: 1024px) { - #debug-bar .ci-label img { - margin: unset; - } - .hide-sm { - display: none !important; - } + #debug-bar .ci-label img { + margin: unset; + } + .hide-sm { + display: none !important; + } } #debug-icon { - background-color: #FFFFFF; - box-shadow: 0 0 4px #DFDFDF; - -moz-box-shadow: 0 0 4px #DFDFDF; - -webkit-box-shadow: 0 0 4px #DFDFDF; + background-color: #ffffff; + box-shadow: 0 0 4px #dfdfdf; + -moz-box-shadow: 0 0 4px #dfdfdf; + -webkit-box-shadow: 0 0 4px #dfdfdf; } #debug-icon a:active, #debug-icon a:link, #debug-icon a:visited { - color: #DD8615; + color: #dd8615; } #debug-bar { - background-color: #FFFFFF; - color: #434343; + background-color: #ffffff; + color: #434343; } #debug-bar h1, #debug-bar h2, @@ -335,217 +341,217 @@ #debug-bar td, #debug-bar button, #debug-bar .toolbar { - background-color: transparent; - color: #434343; + background-color: transparent; + color: #434343; } #debug-bar button { - background-color: #FFFFFF; + background-color: #ffffff; } #debug-bar table strong { - color: #DD8615; + color: #dd8615; } #debug-bar table tbody tr:hover { - background-color: #DFDFDF; + background-color: #dfdfdf; } #debug-bar table tbody tr.current { - background-color: #FDC894; + background-color: #fdc894; } #debug-bar table tbody tr.current:hover td { - background-color: #DD4814; - color: #FFFFFF; + background-color: #dd4814; + color: #ffffff; } #debug-bar .toolbar { - background-color: #FFFFFF; - box-shadow: 0 0 4px #DFDFDF; - -moz-box-shadow: 0 0 4px #DFDFDF; - -webkit-box-shadow: 0 0 4px #DFDFDF; + background-color: #ffffff; + box-shadow: 0 0 4px #dfdfdf; + -moz-box-shadow: 0 0 4px #dfdfdf; + -webkit-box-shadow: 0 0 4px #dfdfdf; } #debug-bar .toolbar img { - filter: brightness(0) invert(0.4); + filter: brightness(0) invert(0.4); } #debug-bar.fixed-top .toolbar { - box-shadow: 0 0 4px #DFDFDF; - -moz-box-shadow: 0 0 4px #DFDFDF; - -webkit-box-shadow: 0 0 4px #DFDFDF; + box-shadow: 0 0 4px #dfdfdf; + -moz-box-shadow: 0 0 4px #dfdfdf; + -webkit-box-shadow: 0 0 4px #dfdfdf; } #debug-bar.fixed-top .tab { - box-shadow: 0 1px 4px #DFDFDF; - -moz-box-shadow: 0 1px 4px #DFDFDF; - -webkit-box-shadow: 0 1px 4px #DFDFDF; + box-shadow: 0 1px 4px #dfdfdf; + -moz-box-shadow: 0 1px 4px #dfdfdf; + -webkit-box-shadow: 0 1px 4px #dfdfdf; } #debug-bar .muted { - color: #434343; + color: #434343; } #debug-bar .muted td { - color: #DFDFDF; + color: #dfdfdf; } #debug-bar .muted:hover td { - color: #434343; + color: #434343; } #debug-bar #toolbar-position, #debug-bar #toolbar-theme { - filter: brightness(0) invert(0.6); + filter: brightness(0) invert(0.6); } #debug-bar .ci-label.active { - background-color: #DFDFDF; + background-color: #dfdfdf; } #debug-bar .ci-label:hover { - background-color: #DFDFDF; + background-color: #dfdfdf; } #debug-bar .ci-label .badge { - background-color: #DD4814; - color: #FFFFFF; + background-color: #dd4814; + color: #ffffff; } #debug-bar .tab { - background-color: #FFFFFF; - box-shadow: 0 -1px 4px #DFDFDF; - -moz-box-shadow: 0 -1px 4px #DFDFDF; - -webkit-box-shadow: 0 -1px 4px #DFDFDF; + background-color: #ffffff; + box-shadow: 0 -1px 4px #dfdfdf; + -moz-box-shadow: 0 -1px 4px #dfdfdf; + -webkit-box-shadow: 0 -1px 4px #dfdfdf; } #debug-bar .timeline th, #debug-bar .timeline td { - border-color: #DFDFDF; + border-color: #dfdfdf; } #debug-bar .timeline .timer { - background-color: #DD8615; + background-color: #dd8615; } .debug-view.show-view { - border-color: #DD8615; + border-color: #dd8615; } .debug-view-path { - background-color: #FDC894; - color: #434343; + background-color: #fdc894; + color: #434343; } @media (prefers-color-scheme: dark) { - #debug-icon { - background-color: #252525; - box-shadow: 0 0 4px #DFDFDF; - -moz-box-shadow: 0 0 4px #DFDFDF; - -webkit-box-shadow: 0 0 4px #DFDFDF; - } - #debug-icon a:active, - #debug-icon a:link, - #debug-icon a:visited { - color: #DD8615; - } - #debug-bar { - background-color: #252525; - color: #DFDFDF; - } - #debug-bar h1, - #debug-bar h2, - #debug-bar h3, - #debug-bar p, - #debug-bar a, - #debug-bar button, - #debug-bar table, - #debug-bar thead, - #debug-bar tr, - #debug-bar td, - #debug-bar button, - #debug-bar .toolbar { - background-color: transparent; - color: #DFDFDF; - } - #debug-bar button { - background-color: #252525; - } - #debug-bar table strong { - color: #DD8615; - } - #debug-bar table tbody tr:hover { - background-color: #434343; - } - #debug-bar table tbody tr.current { - background-color: #FDC894; - } - #debug-bar table tbody tr.current td { - color: #252525; - } - #debug-bar table tbody tr.current:hover td { - background-color: #DD4814; - color: #FFFFFF; - } - #debug-bar .toolbar { - background-color: #434343; - box-shadow: 0 0 4px #434343; - -moz-box-shadow: 0 0 4px #434343; - -webkit-box-shadow: 0 0 4px #434343; - } - #debug-bar .toolbar img { - filter: brightness(0) invert(1); - } - #debug-bar.fixed-top .toolbar { - box-shadow: 0 0 4px #434343; - -moz-box-shadow: 0 0 4px #434343; - -webkit-box-shadow: 0 0 4px #434343; - } - #debug-bar.fixed-top .tab { - box-shadow: 0 1px 4px #434343; - -moz-box-shadow: 0 1px 4px #434343; - -webkit-box-shadow: 0 1px 4px #434343; - } - #debug-bar .muted { - color: #DFDFDF; - } - #debug-bar .muted td { - color: #434343; - } - #debug-bar .muted:hover td { - color: #DFDFDF; - } - #debug-bar #toolbar-position, - #debug-bar #toolbar-theme { - filter: brightness(0) invert(0.6); - } - #debug-bar .ci-label.active { - background-color: #252525; - } - #debug-bar .ci-label:hover { - background-color: #252525; - } - #debug-bar .ci-label .badge { - background-color: #DD4814; - color: #FFFFFF; - } - #debug-bar .tab { - background-color: #252525; - box-shadow: 0 -1px 4px #434343; - -moz-box-shadow: 0 -1px 4px #434343; - -webkit-box-shadow: 0 -1px 4px #434343; - } - #debug-bar .timeline th, - #debug-bar .timeline td { - border-color: #434343; - } - #debug-bar .timeline .timer { - background-color: #DD8615; - } - .debug-view.show-view { - border-color: #DD8615; - } - .debug-view-path { - background-color: #FDC894; - color: #434343; - } + #debug-icon { + background-color: #252525; + box-shadow: 0 0 4px #dfdfdf; + -moz-box-shadow: 0 0 4px #dfdfdf; + -webkit-box-shadow: 0 0 4px #dfdfdf; + } + #debug-icon a:active, + #debug-icon a:link, + #debug-icon a:visited { + color: #dd8615; + } + #debug-bar { + background-color: #252525; + color: #dfdfdf; + } + #debug-bar h1, + #debug-bar h2, + #debug-bar h3, + #debug-bar p, + #debug-bar a, + #debug-bar button, + #debug-bar table, + #debug-bar thead, + #debug-bar tr, + #debug-bar td, + #debug-bar button, + #debug-bar .toolbar { + background-color: transparent; + color: #dfdfdf; + } + #debug-bar button { + background-color: #252525; + } + #debug-bar table strong { + color: #dd8615; + } + #debug-bar table tbody tr:hover { + background-color: #434343; + } + #debug-bar table tbody tr.current { + background-color: #fdc894; + } + #debug-bar table tbody tr.current td { + color: #252525; + } + #debug-bar table tbody tr.current:hover td { + background-color: #dd4814; + color: #ffffff; + } + #debug-bar .toolbar { + background-color: #434343; + box-shadow: 0 0 4px #434343; + -moz-box-shadow: 0 0 4px #434343; + -webkit-box-shadow: 0 0 4px #434343; + } + #debug-bar .toolbar img { + filter: brightness(0) invert(1); + } + #debug-bar.fixed-top .toolbar { + box-shadow: 0 0 4px #434343; + -moz-box-shadow: 0 0 4px #434343; + -webkit-box-shadow: 0 0 4px #434343; + } + #debug-bar.fixed-top .tab { + box-shadow: 0 1px 4px #434343; + -moz-box-shadow: 0 1px 4px #434343; + -webkit-box-shadow: 0 1px 4px #434343; + } + #debug-bar .muted { + color: #dfdfdf; + } + #debug-bar .muted td { + color: #434343; + } + #debug-bar .muted:hover td { + color: #dfdfdf; + } + #debug-bar #toolbar-position, + #debug-bar #toolbar-theme { + filter: brightness(0) invert(0.6); + } + #debug-bar .ci-label.active { + background-color: #252525; + } + #debug-bar .ci-label:hover { + background-color: #252525; + } + #debug-bar .ci-label .badge { + background-color: #dd4814; + color: #ffffff; + } + #debug-bar .tab { + background-color: #252525; + box-shadow: 0 -1px 4px #434343; + -moz-box-shadow: 0 -1px 4px #434343; + -webkit-box-shadow: 0 -1px 4px #434343; + } + #debug-bar .timeline th, + #debug-bar .timeline td { + border-color: #434343; + } + #debug-bar .timeline .timer { + background-color: #dd8615; + } + .debug-view.show-view { + border-color: #dd8615; + } + .debug-view-path { + background-color: #fdc894; + color: #434343; + } } #toolbarContainer.dark #debug-icon { - background-color: #252525; - box-shadow: 0 0 4px #DFDFDF; - -moz-box-shadow: 0 0 4px #DFDFDF; - -webkit-box-shadow: 0 0 4px #DFDFDF; + background-color: #252525; + box-shadow: 0 0 4px #dfdfdf; + -moz-box-shadow: 0 0 4px #dfdfdf; + -webkit-box-shadow: 0 0 4px #dfdfdf; } #toolbarContainer.dark #debug-icon a:active, #toolbarContainer.dark #debug-icon a:link, #toolbarContainer.dark #debug-icon a:visited { - color: #DD8615; + color: #dd8615; } #toolbarContainer.dark #debug-bar { - background-color: #252525; - color: #DFDFDF; + background-color: #252525; + color: #dfdfdf; } #toolbarContainer.dark #debug-bar h1, #toolbarContainer.dark #debug-bar h2, @@ -559,109 +565,109 @@ #toolbarContainer.dark #debug-bar td, #toolbarContainer.dark #debug-bar button, #toolbarContainer.dark #debug-bar .toolbar { - background-color: transparent; - color: #DFDFDF; + background-color: transparent; + color: #dfdfdf; } #toolbarContainer.dark #debug-bar button { - background-color: #252525; + background-color: #252525; } #toolbarContainer.dark #debug-bar table strong { - color: #DD8615; + color: #dd8615; } #toolbarContainer.dark #debug-bar table tbody tr:hover { - background-color: #434343; + background-color: #434343; } #toolbarContainer.dark #debug-bar table tbody tr.current { - background-color: #FDC894; + background-color: #fdc894; } #toolbarContainer.dark #debug-bar table tbody tr.current td { - color: #252525; + color: #252525; } #toolbarContainer.dark #debug-bar table tbody tr.current:hover td { - background-color: #DD4814; - color: #FFFFFF; + background-color: #dd4814; + color: #ffffff; } #toolbarContainer.dark #debug-bar .toolbar { - background-color: #434343; - box-shadow: 0 0 4px #434343; - -moz-box-shadow: 0 0 4px #434343; - -webkit-box-shadow: 0 0 4px #434343; + background-color: #434343; + box-shadow: 0 0 4px #434343; + -moz-box-shadow: 0 0 4px #434343; + -webkit-box-shadow: 0 0 4px #434343; } #toolbarContainer.dark #debug-bar .toolbar img { - filter: brightness(0) invert(1); + filter: brightness(0) invert(1); } #toolbarContainer.dark #debug-bar.fixed-top .toolbar { - box-shadow: 0 0 4px #434343; - -moz-box-shadow: 0 0 4px #434343; - -webkit-box-shadow: 0 0 4px #434343; + box-shadow: 0 0 4px #434343; + -moz-box-shadow: 0 0 4px #434343; + -webkit-box-shadow: 0 0 4px #434343; } #toolbarContainer.dark #debug-bar.fixed-top .tab { - box-shadow: 0 1px 4px #434343; - -moz-box-shadow: 0 1px 4px #434343; - -webkit-box-shadow: 0 1px 4px #434343; + box-shadow: 0 1px 4px #434343; + -moz-box-shadow: 0 1px 4px #434343; + -webkit-box-shadow: 0 1px 4px #434343; } #toolbarContainer.dark #debug-bar .muted { - color: #DFDFDF; + color: #dfdfdf; } #toolbarContainer.dark #debug-bar .muted td { - color: #434343; + color: #434343; } #toolbarContainer.dark #debug-bar .muted:hover td { - color: #DFDFDF; + color: #dfdfdf; } #toolbarContainer.dark #debug-bar #toolbar-position, #toolbarContainer.dark #debug-bar #toolbar-theme { - filter: brightness(0) invert(0.6); + filter: brightness(0) invert(0.6); } #toolbarContainer.dark #debug-bar .ci-label.active { - background-color: #252525; + background-color: #252525; } #toolbarContainer.dark #debug-bar .ci-label:hover { - background-color: #252525; + background-color: #252525; } #toolbarContainer.dark #debug-bar .ci-label .badge { - background-color: #DD4814; - color: #FFFFFF; + background-color: #dd4814; + color: #ffffff; } #toolbarContainer.dark #debug-bar .tab { - background-color: #252525; - box-shadow: 0 -1px 4px #434343; - -moz-box-shadow: 0 -1px 4px #434343; - -webkit-box-shadow: 0 -1px 4px #434343; + background-color: #252525; + box-shadow: 0 -1px 4px #434343; + -moz-box-shadow: 0 -1px 4px #434343; + -webkit-box-shadow: 0 -1px 4px #434343; } #toolbarContainer.dark #debug-bar .timeline th, #toolbarContainer.dark #debug-bar .timeline td { - border-color: #434343; + border-color: #434343; } #toolbarContainer.dark #debug-bar .timeline .timer { - background-color: #DD8615; + background-color: #dd8615; } #toolbarContainer.dark .debug-view.show-view { - border-color: #DD8615; + border-color: #dd8615; } #toolbarContainer.dark .debug-view-path { - background-color: #FDC894; - color: #434343; + background-color: #fdc894; + color: #434343; } -#toolbarContainer.dark td[data-debugbar-route] input[type=text] { - background: #000; - color: #fff; +#toolbarContainer.dark td[data-debugbar-route] input[type="text"] { + background: #000; + color: #fff; } #toolbarContainer.light #debug-icon { - background-color: #FFFFFF; - box-shadow: 0 0 4px #DFDFDF; - -moz-box-shadow: 0 0 4px #DFDFDF; - -webkit-box-shadow: 0 0 4px #DFDFDF; + background-color: #ffffff; + box-shadow: 0 0 4px #dfdfdf; + -moz-box-shadow: 0 0 4px #dfdfdf; + -webkit-box-shadow: 0 0 4px #dfdfdf; } #toolbarContainer.light #debug-icon a:active, #toolbarContainer.light #debug-icon a:link, #toolbarContainer.light #debug-icon a:visited { - color: #DD8615; + color: #dd8615; } #toolbarContainer.light #debug-bar { - background-color: #FFFFFF; - color: #434343; + background-color: #ffffff; + color: #434343; } #toolbarContainer.light #debug-bar h1, #toolbarContainer.light #debug-bar h2, @@ -675,124 +681,134 @@ #toolbarContainer.light #debug-bar td, #toolbarContainer.light #debug-bar button, #toolbarContainer.light #debug-bar .toolbar { - background-color: transparent; - color: #434343; + background-color: transparent; + color: #434343; } #toolbarContainer.light #debug-bar button { - background-color: #FFFFFF; + background-color: #ffffff; } #toolbarContainer.light #debug-bar table strong { - color: #DD8615; + color: #dd8615; } #toolbarContainer.light #debug-bar table tbody tr:hover { - background-color: #DFDFDF; + background-color: #dfdfdf; } #toolbarContainer.light #debug-bar table tbody tr.current { - background-color: #FDC894; + background-color: #fdc894; } #toolbarContainer.light #debug-bar table tbody tr.current:hover td { - background-color: #DD4814; - color: #FFFFFF; + background-color: #dd4814; + color: #ffffff; } #toolbarContainer.light #debug-bar .toolbar { - background-color: #FFFFFF; - box-shadow: 0 0 4px #DFDFDF; - -moz-box-shadow: 0 0 4px #DFDFDF; - -webkit-box-shadow: 0 0 4px #DFDFDF; + background-color: #ffffff; + box-shadow: 0 0 4px #dfdfdf; + -moz-box-shadow: 0 0 4px #dfdfdf; + -webkit-box-shadow: 0 0 4px #dfdfdf; } #toolbarContainer.light #debug-bar .toolbar img { - filter: brightness(0) invert(0.4); + filter: brightness(0) invert(0.4); } #toolbarContainer.light #debug-bar.fixed-top .toolbar { - box-shadow: 0 0 4px #DFDFDF; - -moz-box-shadow: 0 0 4px #DFDFDF; - -webkit-box-shadow: 0 0 4px #DFDFDF; + box-shadow: 0 0 4px #dfdfdf; + -moz-box-shadow: 0 0 4px #dfdfdf; + -webkit-box-shadow: 0 0 4px #dfdfdf; } #toolbarContainer.light #debug-bar.fixed-top .tab { - box-shadow: 0 1px 4px #DFDFDF; - -moz-box-shadow: 0 1px 4px #DFDFDF; - -webkit-box-shadow: 0 1px 4px #DFDFDF; + box-shadow: 0 1px 4px #dfdfdf; + -moz-box-shadow: 0 1px 4px #dfdfdf; + -webkit-box-shadow: 0 1px 4px #dfdfdf; } #toolbarContainer.light #debug-bar .muted { - color: #434343; + color: #434343; } #toolbarContainer.light #debug-bar .muted td { - color: #DFDFDF; + color: #dfdfdf; } #toolbarContainer.light #debug-bar .muted:hover td { - color: #434343; + color: #434343; } #toolbarContainer.light #debug-bar #toolbar-position, #toolbarContainer.light #debug-bar #toolbar-theme { - filter: brightness(0) invert(0.6); + filter: brightness(0) invert(0.6); } #toolbarContainer.light #debug-bar .ci-label.active { - background-color: #DFDFDF; + background-color: #dfdfdf; } #toolbarContainer.light #debug-bar .ci-label:hover { - background-color: #DFDFDF; + background-color: #dfdfdf; } #toolbarContainer.light #debug-bar .ci-label .badge { - background-color: #DD4814; - color: #FFFFFF; + background-color: #dd4814; + color: #ffffff; } #toolbarContainer.light #debug-bar .tab { - background-color: #FFFFFF; - box-shadow: 0 -1px 4px #DFDFDF; - -moz-box-shadow: 0 -1px 4px #DFDFDF; - -webkit-box-shadow: 0 -1px 4px #DFDFDF; + background-color: #ffffff; + box-shadow: 0 -1px 4px #dfdfdf; + -moz-box-shadow: 0 -1px 4px #dfdfdf; + -webkit-box-shadow: 0 -1px 4px #dfdfdf; } #toolbarContainer.light #debug-bar .timeline th, #toolbarContainer.light #debug-bar .timeline td { - border-color: #DFDFDF; + border-color: #dfdfdf; } #toolbarContainer.light #debug-bar .timeline .timer { - background-color: #DD8615; + background-color: #dd8615; } #toolbarContainer.light .debug-view.show-view { - border-color: #DD8615; + border-color: #dd8615; } #toolbarContainer.light .debug-view-path { - background-color: #FDC894; - color: #434343; + background-color: #fdc894; + color: #434343; } .debug-bar-width30 { - width: 30%; + width: 30%; } .debug-bar-width10 { - width: 10%; + width: 10%; } .debug-bar-width70p { - width: 70px; + width: 70px; } .debug-bar-width190p { - width: 190px; + width: 190px; } .debug-bar-width20e { - width: 20em; + width: 20em; } .debug-bar-width6r { - width: 6rem; + width: 6rem; } .debug-bar-ndisplay { - display: none; + display: none; } .debug-bar-alignRight { - text-align: right; + text-align: right; } .debug-bar-alignLeft { - text-align: left; + text-align: left; } .debug-bar-noverflow { - overflow: hidden; + overflow: hidden; +} + +/* ENDLESS ROTATE */ +.rotate { + animation: rotate 9s linear infinite; +} +@keyframes rotate { + to { + transform: rotate(360deg); + } } diff --git a/system/Debug/Toolbar/Views/toolbar.js b/system/Debug/Toolbar/Views/toolbar.js index 7805a99dda05..bfff574507e7 100644 --- a/system/Debug/Toolbar/Views/toolbar.js +++ b/system/Debug/Toolbar/Views/toolbar.js @@ -3,15 +3,14 @@ */ var ciDebugBar = { + toolbarContainer: null, + toolbar: null, + icon: null, - toolbarContainer : null, - toolbar : null, - icon : null, - - init : function () { - this.toolbarContainer = document.getElementById('toolbarContainer'); - this.toolbar = document.getElementById('debug-bar'); - this.icon = document.getElementById('debug-icon'); + init: function () { + this.toolbarContainer = document.getElementById("toolbarContainer"); + this.toolbar = document.getElementById("debug-bar"); + this.icon = document.getElementById("debug-icon"); ciDebugBar.createListeners(); ciDebugBar.setToolbarState(); @@ -19,115 +18,123 @@ var ciDebugBar = { ciDebugBar.setToolbarTheme(); ciDebugBar.toggleViewsHints(); ciDebugBar.routerLink(); + ciDebugBar.setHotReloadState(); - document.getElementById('debug-bar-link').addEventListener('click', ciDebugBar.toggleToolbar, true); - document.getElementById('debug-icon-link').addEventListener('click', ciDebugBar.toggleToolbar, true); + document + .getElementById("debug-bar-link") + .addEventListener("click", ciDebugBar.toggleToolbar, true); + document + .getElementById("debug-icon-link") + .addEventListener("click", ciDebugBar.toggleToolbar, true); // Allows to highlight the row of the current history request - var btn = this.toolbar.querySelector('button[data-time="' + localStorage.getItem('debugbar-time') + '"]'); - ciDebugBar.addClass(btn.parentNode.parentNode, 'current'); - - historyLoad = this.toolbar.getElementsByClassName('ci-history-load'); - - for (var i = 0; i < historyLoad.length; i++) - { - historyLoad[i].addEventListener('click', function () { - loadDoc(this.getAttribute('data-time')); - }, true); + var btn = this.toolbar.querySelector( + 'button[data-time="' + localStorage.getItem("debugbar-time") + '"]' + ); + ciDebugBar.addClass(btn.parentNode.parentNode, "current"); + + historyLoad = this.toolbar.getElementsByClassName("ci-history-load"); + + for (var i = 0; i < historyLoad.length; i++) { + historyLoad[i].addEventListener( + "click", + function () { + loadDoc(this.getAttribute("data-time")); + }, + true + ); } // Display the active Tab on page load - var tab = ciDebugBar.readCookie('debug-bar-tab'); - if (document.getElementById(tab)) - { - var el = document.getElementById(tab); - el.style.display = 'block'; - ciDebugBar.addClass(el, 'active'); - tab = document.querySelector('[data-tab=' + tab + ']'); - if (tab) - { - ciDebugBar.addClass(tab.parentNode, 'active'); + var tab = ciDebugBar.readCookie("debug-bar-tab"); + if (document.getElementById(tab)) { + var el = document.getElementById(tab); + el.style.display = "block"; + ciDebugBar.addClass(el, "active"); + tab = document.querySelector("[data-tab=" + tab + "]"); + if (tab) { + ciDebugBar.addClass(tab.parentNode, "active"); } } }, - createListeners : function () { - var buttons = [].slice.call(this.toolbar.querySelectorAll('.ci-label a')); + createListeners: function () { + var buttons = [].slice.call( + this.toolbar.querySelectorAll(".ci-label a") + ); - for (var i = 0; i < buttons.length; i++) - { - buttons[i].addEventListener('click', ciDebugBar.showTab, true); + for (var i = 0; i < buttons.length; i++) { + buttons[i].addEventListener("click", ciDebugBar.showTab, true); } // Hook up generic toggle via data attributes `data-toggle="foo"` - var links = this.toolbar.querySelectorAll('[data-toggle]'); - for (var i = 0; i < links.length; i++) - { - links[i].addEventListener('click', ciDebugBar.toggleRows, true); + var links = this.toolbar.querySelectorAll("[data-toggle]"); + for (var i = 0; i < links.length; i++) { + links[i].addEventListener("click", ciDebugBar.toggleRows, true); } }, showTab: function () { // Get the target tab, if any - var tab = document.getElementById(this.getAttribute('data-tab')); + var tab = document.getElementById(this.getAttribute("data-tab")); // If the label have not a tab stops here - if (! tab) - { + if (!tab) { return; } // Remove debug-bar-tab cookie - ciDebugBar.createCookie('debug-bar-tab', '', -1); + ciDebugBar.createCookie("debug-bar-tab", "", -1); // Check our current state. var state = tab.style.display; // Hide all tabs - var tabs = document.querySelectorAll('#debug-bar .tab'); + var tabs = document.querySelectorAll("#debug-bar .tab"); - for (var i = 0; i < tabs.length; i++) - { - tabs[i].style.display = 'none'; + for (var i = 0; i < tabs.length; i++) { + tabs[i].style.display = "none"; } // Mark all labels as inactive - var labels = document.querySelectorAll('#debug-bar .ci-label'); + var labels = document.querySelectorAll("#debug-bar .ci-label"); - for (var i = 0; i < labels.length; i++) - { - ciDebugBar.removeClass(labels[i], 'active'); + for (var i = 0; i < labels.length; i++) { + ciDebugBar.removeClass(labels[i], "active"); } // Show/hide the selected tab - if (state != 'block') - { - tab.style.display = 'block'; - ciDebugBar.addClass(this.parentNode, 'active'); + if (state != "block") { + tab.style.display = "block"; + ciDebugBar.addClass(this.parentNode, "active"); // Create debug-bar-tab cookie to persistent state - ciDebugBar.createCookie('debug-bar-tab', this.getAttribute('data-tab'), 365); + ciDebugBar.createCookie( + "debug-bar-tab", + this.getAttribute("data-tab"), + 365 + ); } }, - addClass : function (el, className) { - if (el.classList) - { + addClass: function (el, className) { + if (el.classList) { el.classList.add(className); - } - else - { - el.className += ' ' + className; + } else { + el.className += " " + className; } }, - removeClass : function (el, className) { - if (el.classList) - { + removeClass: function (el, className) { + if (el.classList) { el.classList.remove(className); - } - else - { - el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); + } else { + el.className = el.className.replace( + new RegExp( + "(^|\\b)" + className.split(" ").join("|") + "(\\b|$)", + "gi" + ), + " " + ); } }, @@ -137,12 +144,14 @@ var ciDebugBar = { * * @param event */ - toggleRows : function(event) { - if(event.target) - { - let row = event.target.closest('tr'); - let target = document.getElementById(row.getAttribute('data-toggle')); - target.style.display = target.style.display === 'none' ? 'table-row' : 'none'; + toggleRows: function (event) { + if (event.target) { + let row = event.target.closest("tr"); + let target = document.getElementById( + row.getAttribute("data-toggle") + ); + target.style.display = + target.style.display === "none" ? "table-row" : "none"; } }, @@ -151,15 +160,13 @@ var ciDebugBar = { * * @param obj */ - toggleDataTable : function (obj) { - if (typeof obj == 'string') - { - obj = document.getElementById(obj + '_table'); + toggleDataTable: function (obj) { + if (typeof obj == "string") { + obj = document.getElementById(obj + "_table"); } - if (obj) - { - obj.style.display = obj.style.display === 'none' ? 'block' : 'none'; + if (obj) { + obj.style.display = obj.style.display === "none" ? "block" : "none"; } }, @@ -168,35 +175,37 @@ var ciDebugBar = { * * @param obj */ - toggleChildRows : function (obj) { - if (typeof obj == 'string') - { - par = document.getElementById(obj + '_parent') - obj = document.getElementById(obj + '_children'); + toggleChildRows: function (obj) { + if (typeof obj == "string") { + par = document.getElementById(obj + "_parent"); + obj = document.getElementById(obj + "_children"); } - if (par && obj) - { - obj.style.display = obj.style.display === 'none' ? '' : 'none'; - par.classList.toggle('timeline-parent-open'); + if (par && obj) { + obj.style.display = obj.style.display === "none" ? "" : "none"; + par.classList.toggle("timeline-parent-open"); } }, - //-------------------------------------------------------------------- /** * Toggle tool bar from full to icon and icon to full */ - toggleToolbar : function () { - var open = ciDebugBar.toolbar.style.display != 'none'; + toggleToolbar: function () { + var open = ciDebugBar.toolbar.style.display != "none"; - ciDebugBar.icon.style.display = open == true ? 'inline-block' : 'none'; - ciDebugBar.toolbar.style.display = open == false ? 'inline-block' : 'none'; + ciDebugBar.icon.style.display = open == true ? "inline-block" : "none"; + ciDebugBar.toolbar.style.display = + open == false ? "inline-block" : "none"; // Remember it for other page loads on this site - ciDebugBar.createCookie('debug-bar-state', '', -1); - ciDebugBar.createCookie('debug-bar-state', open == true ? 'minimized' : 'open' , 365); + ciDebugBar.createCookie("debug-bar-state", "", -1); + ciDebugBar.createCookie( + "debug-bar-state", + open == true ? "minimized" : "open", + 365 + ); }, /** @@ -204,49 +213,58 @@ var ciDebugBar = { * the page is first loaded to allow it to remember the state between refreshes. */ setToolbarState: function () { - var open = ciDebugBar.readCookie('debug-bar-state'); + var open = ciDebugBar.readCookie("debug-bar-state"); - ciDebugBar.icon.style.display = open != 'open' ? 'inline-block' : 'none'; - ciDebugBar.toolbar.style.display = open == 'open' ? 'inline-block' : 'none'; + ciDebugBar.icon.style.display = + open != "open" ? "inline-block" : "none"; + ciDebugBar.toolbar.style.display = + open == "open" ? "inline-block" : "none"; }, toggleViewsHints: function () { // Avoid toggle hints on history requests that are not the initial - if (localStorage.getItem('debugbar-time') != localStorage.getItem('debugbar-time-new')) - { - var a = document.querySelector('a[data-tab="ci-views"]'); - a.href = '#'; + if ( + localStorage.getItem("debugbar-time") != + localStorage.getItem("debugbar-time-new") + ) { + var a = document.querySelector('a[data-tab="ci-views"]'); + a.href = "#"; return; } - var nodeList = []; // [ Element, NewElement( 1 )/OldElement( 0 ) ] + var nodeList = []; // [ Element, NewElement( 1 )/OldElement( 0 ) ] var sortedComments = []; - var comments = []; + var comments = []; var getComments = function () { - var nodes = []; - var result = []; - var xpathResults = document.evaluate( "//comment()[starts-with(., ' DEBUG-VIEW')]", document, null, XPathResult.ANY_TYPE, null); - var nextNode = xpathResults.iterateNext(); - while ( nextNode ) - { - nodes.push( nextNode ); + var nodes = []; + var result = []; + var xpathResults = document.evaluate( + "//comment()[starts-with(., ' DEBUG-VIEW')]", + document, + null, + XPathResult.ANY_TYPE, + null + ); + var nextNode = xpathResults.iterateNext(); + while (nextNode) { + nodes.push(nextNode); nextNode = xpathResults.iterateNext(); } // sort comment by opening and closing tags - for (var i = 0; i < nodes.length; ++i) - { + for (var i = 0; i < nodes.length; ++i) { // get file path + name to use as key - var path = nodes[i].nodeValue.substring( 18, nodes[i].nodeValue.length - 1 ); + var path = nodes[i].nodeValue.substring( + 18, + nodes[i].nodeValue.length - 1 + ); - if ( nodes[i].nodeValue[12] === 'S' ) // simple check for start comment - { + if (nodes[i].nodeValue[12] === "S") { + // simple check for start comment // create new entry - result[path] = [ nodes[i], null ]; - } - else if (result[path]) - { + result[path] = [nodes[i], null]; + } else if (result[path]) { // add to existing entry result[path][1] = nodes[i]; } @@ -256,73 +274,81 @@ var ciDebugBar = { }; // find node that has TargetNode as parentNode - var getParentNode = function ( node, targetNode ) { - if ( node.parentNode === null ) - { + var getParentNode = function (node, targetNode) { + if (node.parentNode === null) { return null; } - if ( node.parentNode !== targetNode ) - { - return getParentNode( node.parentNode, targetNode ); + if (node.parentNode !== targetNode) { + return getParentNode(node.parentNode, targetNode); } return node; }; // define invalid & outer ( also invalid ) elements - const INVALID_ELEMENTS = [ 'NOSCRIPT', 'SCRIPT', 'STYLE' ]; - const OUTER_ELEMENTS = [ 'HTML', 'BODY', 'HEAD' ]; + const INVALID_ELEMENTS = ["NOSCRIPT", "SCRIPT", "STYLE"]; + const OUTER_ELEMENTS = ["HTML", "BODY", "HEAD"]; - var getValidElementInner = function ( node, reverse ) { + var getValidElementInner = function (node, reverse) { // handle invalid tags - if ( OUTER_ELEMENTS.indexOf( node.nodeName ) !== -1 ) - { - for (var i = 0; i < document.body.children.length; ++i) - { - var index = reverse ? document.body.children.length - ( i + 1 ) : i; + if (OUTER_ELEMENTS.indexOf(node.nodeName) !== -1) { + for (var i = 0; i < document.body.children.length; ++i) { + var index = reverse + ? document.body.children.length - (i + 1) + : i; var element = document.body.children[index]; // skip invalid tags - if ( INVALID_ELEMENTS.indexOf( element.nodeName ) !== -1 ) - { + if (INVALID_ELEMENTS.indexOf(element.nodeName) !== -1) { continue; } - return [ element, reverse ]; + return [element, reverse]; } return null; } // get to next valid element - while ( node !== null && INVALID_ELEMENTS.indexOf( node.nodeName ) !== -1 ) - { - node = reverse ? node.previousElementSibling : node.nextElementSibling; + while ( + node !== null && + INVALID_ELEMENTS.indexOf(node.nodeName) !== -1 + ) { + node = reverse + ? node.previousElementSibling + : node.nextElementSibling; } // return non array if we couldnt find something - if ( node === null ) - { + if (node === null) { return null; } - return [ node, reverse ]; + return [node, reverse]; }; // get next valid element ( to be safe to add divs ) // @return [ element, skip element ] or null if we couldnt find a valid place - var getValidElement = function ( nodeElement ) { - if (nodeElement) - { - if ( nodeElement.nextElementSibling !== null ) - { - return getValidElementInner( nodeElement.nextElementSibling, false ) - || getValidElementInner( nodeElement.previousElementSibling, true ); + var getValidElement = function (nodeElement) { + if (nodeElement) { + if (nodeElement.nextElementSibling !== null) { + return ( + getValidElementInner( + nodeElement.nextElementSibling, + false + ) || + getValidElementInner( + nodeElement.previousElementSibling, + true + ) + ); } - if ( nodeElement.previousElementSibling !== null ) - { - return getValidElementInner( nodeElement.previousElementSibling, true ); + if (nodeElement.previousElementSibling !== null) { + return getValidElementInner( + nodeElement.previousElementSibling, + true + ); } } @@ -330,89 +356,80 @@ var ciDebugBar = { return null; }; - function showHints() - { + function showHints() { // Had AJAX? Reset view blocks sortedComments = getComments(); - for (var key in sortedComments) - { - var startElement = getValidElement( sortedComments[key][0] ); - var endElement = getValidElement( sortedComments[key][1] ); + for (var key in sortedComments) { + var startElement = getValidElement(sortedComments[key][0]); + var endElement = getValidElement(sortedComments[key][1]); // skip if we couldnt get a valid element - if ( startElement === null || endElement === null ) - { + if (startElement === null || endElement === null) { continue; } // find element which has same parent as startelement - var jointParent = getParentNode( endElement[0], startElement[0].parentNode ); - if ( jointParent === null ) - { + var jointParent = getParentNode( + endElement[0], + startElement[0].parentNode + ); + if (jointParent === null) { // find element which has same parent as endelement - jointParent = getParentNode( startElement[0], endElement[0].parentNode ); - if ( jointParent === null ) - { + jointParent = getParentNode( + startElement[0], + endElement[0].parentNode + ); + if (jointParent === null) { // both tries failed continue; - } - else - { + } else { startElement[0] = jointParent; } - } - else - { + } else { endElement[0] = jointParent; } - var debugDiv = document.createElement( 'div' ); // holder - var debugPath = document.createElement( 'div' ); // path + var debugDiv = document.createElement("div"); // holder + var debugPath = document.createElement("div"); // path var childArray = startElement[0].parentNode.childNodes; // target child array - var parent = startElement[0].parentNode; + var parent = startElement[0].parentNode; var start, end; // setup container - debugDiv.classList.add( 'debug-view' ); - debugDiv.classList.add( 'show-view' ); - debugPath.classList.add( 'debug-view-path' ); + debugDiv.classList.add("debug-view"); + debugDiv.classList.add("show-view"); + debugPath.classList.add("debug-view-path"); debugPath.innerText = key; - debugDiv.appendChild( debugPath ); + debugDiv.appendChild(debugPath); // calc distance between them // start - for (var i = 0; i < childArray.length; ++i) - { + for (var i = 0; i < childArray.length; ++i) { // check for comment ( start & end ) -> if its before valid start element - if ( childArray[i] === sortedComments[key][1] || + if ( + childArray[i] === sortedComments[key][1] || childArray[i] === sortedComments[key][0] || - childArray[i] === startElement[0] ) - { + childArray[i] === startElement[0] + ) { start = i; - if ( childArray[i] === sortedComments[key][0] ) - { + if (childArray[i] === sortedComments[key][0]) { start++; // increase to skip the start comment } break; } } // adjust if we want to skip the start element - if ( startElement[1] ) - { + if (startElement[1]) { start++; } // end - for (var i = start; i < childArray.length; ++i) - { - if ( childArray[i] === endElement[0] ) - { + for (var i = start; i < childArray.length; ++i) { + if (childArray[i] === endElement[0]) { end = i; // dont break to check for end comment after end valid element - } - else if ( childArray[i] === sortedComments[key][1] ) - { + } else if (childArray[i] === sortedComments[key][1]) { // if we found the end comment, we can break end = i; break; @@ -421,161 +438,230 @@ var ciDebugBar = { // move elements var number = end - start; - if ( endElement[1] ) - { + if (endElement[1]) { number++; } - for (var i = 0; i < number; ++i) - { - if ( INVALID_ELEMENTS.indexOf( childArray[start] ) !== -1 ) - { + for (var i = 0; i < number; ++i) { + if (INVALID_ELEMENTS.indexOf(childArray[start]) !== -1) { // skip invalid childs that can cause problems if moved start++; continue; } - debugDiv.appendChild( childArray[start] ); + debugDiv.appendChild(childArray[start]); } // add container to DOM - nodeList.push( parent.insertBefore( debugDiv, childArray[start] ) ); + nodeList.push(parent.insertBefore(debugDiv, childArray[start])); } - ciDebugBar.createCookie('debug-view', 'show', 365); - ciDebugBar.addClass(btn, 'active'); + ciDebugBar.createCookie("debug-view", "show", 365); + ciDebugBar.addClass(btn, "active"); } - function hideHints() - { - for (var i = 0; i < nodeList.length; ++i) - { + function hideHints() { + for (var i = 0; i < nodeList.length; ++i) { var index; // find index - for (var j = 0; j < nodeList[i].parentNode.childNodes.length; ++j) - { - if ( nodeList[i].parentNode.childNodes[j] === nodeList[i] ) - { + for ( + var j = 0; + j < nodeList[i].parentNode.childNodes.length; + ++j + ) { + if (nodeList[i].parentNode.childNodes[j] === nodeList[i]) { index = j; break; } } // move child back - while ( nodeList[i].childNodes.length !== 1 ) - { - nodeList[i].parentNode.insertBefore( nodeList[i].childNodes[1], nodeList[i].parentNode.childNodes[index].nextSibling ); + while (nodeList[i].childNodes.length !== 1) { + nodeList[i].parentNode.insertBefore( + nodeList[i].childNodes[1], + nodeList[i].parentNode.childNodes[index].nextSibling + ); index++; } - nodeList[i].parentNode.removeChild( nodeList[i] ); + nodeList[i].parentNode.removeChild(nodeList[i]); } nodeList.length = 0; - ciDebugBar.createCookie('debug-view', '', -1); - ciDebugBar.removeClass(btn, 'active'); + ciDebugBar.createCookie("debug-view", "", -1); + ciDebugBar.removeClass(btn, "active"); } - var btn = document.querySelector('[data-tab=ci-views]'); + var btn = document.querySelector("[data-tab=ci-views]"); // If the Views Collector is inactive stops here - if (! btn) - { + if (!btn) { return; } btn.parentNode.onclick = function () { - if (ciDebugBar.readCookie('debug-view')) - { + if (ciDebugBar.readCookie("debug-view")) { hideHints(); - } - else - { + } else { showHints(); } }; // Determine Hints state on page load - if (ciDebugBar.readCookie('debug-view')) - { + if (ciDebugBar.readCookie("debug-view")) { showHints(); } }, setToolbarPosition: function () { - var btnPosition = this.toolbar.querySelector('#toolbar-position'); + var btnPosition = this.toolbar.querySelector("#toolbar-position"); - if (ciDebugBar.readCookie('debug-bar-position') === 'top') - { - ciDebugBar.addClass(ciDebugBar.icon, 'fixed-top'); - ciDebugBar.addClass(ciDebugBar.toolbar, 'fixed-top'); + if (ciDebugBar.readCookie("debug-bar-position") === "top") { + ciDebugBar.addClass(ciDebugBar.icon, "fixed-top"); + ciDebugBar.addClass(ciDebugBar.toolbar, "fixed-top"); } - btnPosition.addEventListener('click', function () { - var position = ciDebugBar.readCookie('debug-bar-position'); - - ciDebugBar.createCookie('debug-bar-position', '', -1); - - if (!position || position === 'bottom') - { - ciDebugBar.createCookie('debug-bar-position', 'top', 365); - ciDebugBar.addClass(ciDebugBar.icon, 'fixed-top'); - ciDebugBar.addClass(ciDebugBar.toolbar, 'fixed-top'); - } - else - { - ciDebugBar.createCookie('debug-bar-position', 'bottom', 365); - ciDebugBar.removeClass(ciDebugBar.icon, 'fixed-top'); - ciDebugBar.removeClass(ciDebugBar.toolbar, 'fixed-top'); - } - }, true); + btnPosition.addEventListener( + "click", + function () { + var position = ciDebugBar.readCookie("debug-bar-position"); + + ciDebugBar.createCookie("debug-bar-position", "", -1); + + if (!position || position === "bottom") { + ciDebugBar.createCookie("debug-bar-position", "top", 365); + ciDebugBar.addClass(ciDebugBar.icon, "fixed-top"); + ciDebugBar.addClass(ciDebugBar.toolbar, "fixed-top"); + } else { + ciDebugBar.createCookie( + "debug-bar-position", + "bottom", + 365 + ); + ciDebugBar.removeClass(ciDebugBar.icon, "fixed-top"); + ciDebugBar.removeClass(ciDebugBar.toolbar, "fixed-top"); + } + }, + true + ); }, setToolbarTheme: function () { - var btnTheme = this.toolbar.querySelector('#toolbar-theme'); - var isDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches; - var isLightMode = window.matchMedia("(prefers-color-scheme: light)").matches; + var btnTheme = this.toolbar.querySelector("#toolbar-theme"); + var isDarkMode = window.matchMedia( + "(prefers-color-scheme: dark)" + ).matches; + var isLightMode = window.matchMedia( + "(prefers-color-scheme: light)" + ).matches; // If a cookie is set with a value, we force the color scheme - if (ciDebugBar.readCookie('debug-bar-theme') === 'dark') - { - ciDebugBar.removeClass(ciDebugBar.toolbarContainer, 'light'); - ciDebugBar.addClass(ciDebugBar.toolbarContainer, 'dark'); + if (ciDebugBar.readCookie("debug-bar-theme") === "dark") { + ciDebugBar.removeClass(ciDebugBar.toolbarContainer, "light"); + ciDebugBar.addClass(ciDebugBar.toolbarContainer, "dark"); + } else if (ciDebugBar.readCookie("debug-bar-theme") === "light") { + ciDebugBar.removeClass(ciDebugBar.toolbarContainer, "dark"); + ciDebugBar.addClass(ciDebugBar.toolbarContainer, "light"); } - else if (ciDebugBar.readCookie('debug-bar-theme') === 'light') - { - ciDebugBar.removeClass(ciDebugBar.toolbarContainer, 'dark'); - ciDebugBar.addClass(ciDebugBar.toolbarContainer, 'light'); + + btnTheme.addEventListener( + "click", + function () { + var theme = ciDebugBar.readCookie("debug-bar-theme"); + + if ( + !theme && + window.matchMedia("(prefers-color-scheme: dark)").matches + ) { + // If there is no cookie, and "prefers-color-scheme" is set to "dark" + // It means that the user wants to switch to light mode + ciDebugBar.createCookie("debug-bar-theme", "light", 365); + ciDebugBar.removeClass(ciDebugBar.toolbarContainer, "dark"); + ciDebugBar.addClass(ciDebugBar.toolbarContainer, "light"); + } else { + if (theme === "dark") { + ciDebugBar.createCookie( + "debug-bar-theme", + "light", + 365 + ); + ciDebugBar.removeClass( + ciDebugBar.toolbarContainer, + "dark" + ); + ciDebugBar.addClass( + ciDebugBar.toolbarContainer, + "light" + ); + } else { + // In any other cases: if there is no cookie, or the cookie is set to + // "light", or the "prefers-color-scheme" is "light"... + ciDebugBar.createCookie("debug-bar-theme", "dark", 365); + ciDebugBar.removeClass( + ciDebugBar.toolbarContainer, + "light" + ); + ciDebugBar.addClass( + ciDebugBar.toolbarContainer, + "dark" + ); + } + } + }, + true + ); + }, + + setHotReloadState: function () { + var btn = document.getElementById("debug-hot-reload").parentNode; + var btnImg = btn.firstElementChild; + var eventSource; + + // If the Hot Reload Collector is inactive stops here + if (!btn) { + return; } - btnTheme.addEventListener('click', function () { - var theme = ciDebugBar.readCookie('debug-bar-theme'); + btn.onclick = function () { + if (ciDebugBar.readCookie("debug-hot-reload")) { + ciDebugBar.createCookie("debug-hot-reload", "", -1); + ciDebugBar.removeClass(btn, "active"); + ciDebugBar.removeClass(btnImg, "rotate"); - if (!theme && window.matchMedia("(prefers-color-scheme: dark)").matches) - { - // If there is no cookie, and "prefers-color-scheme" is set to "dark" - // It means that the user wants to switch to light mode - ciDebugBar.createCookie('debug-bar-theme', 'light', 365); - ciDebugBar.removeClass(ciDebugBar.toolbarContainer, 'dark'); - ciDebugBar.addClass(ciDebugBar.toolbarContainer, 'light'); - } - else - { - if (theme === 'dark') - { - ciDebugBar.createCookie('debug-bar-theme', 'light', 365); - ciDebugBar.removeClass(ciDebugBar.toolbarContainer, 'dark'); - ciDebugBar.addClass(ciDebugBar.toolbarContainer, 'light'); - } - else - { - // In any other cases: if there is no cookie, or the cookie is set to - // "light", or the "prefers-color-scheme" is "light"... - ciDebugBar.createCookie('debug-bar-theme', 'dark', 365); - ciDebugBar.removeClass(ciDebugBar.toolbarContainer, 'light'); - ciDebugBar.addClass(ciDebugBar.toolbarContainer, 'dark'); + // Close the EventSource connection if it exists + if (typeof eventSource !== "undefined") { + eventSource.close(); + eventSource = void 0; // Undefine the variable } + } else { + ciDebugBar.createCookie("debug-hot-reload", "show", 365); + ciDebugBar.addClass(btn, "active"); + ciDebugBar.addClass(btnImg, "rotate"); + + eventSource = ciDebugBar.hotReloadConnect(); } - }, true); + }; + + // Determine Hot Reload state on page load + if (ciDebugBar.readCookie("debug-hot-reload")) { + ciDebugBar.addClass(btn, "active"); + ciDebugBar.addClass(btnImg, "rotate"); + eventSource = ciDebugBar.hotReloadConnect(); + } + }, + + hotReloadConnect: function () { + const eventSource = new EventSource("/__hot-reload"); + + eventSource.addEventListener("reload", function (e) { + console.log("reload", e); + window.location.reload(); + }); + + eventSource.onerror = (err) => { + console.error("EventSource failed:", err); + }; + + return eventSource; }, /** @@ -585,103 +671,112 @@ var ciDebugBar = { * @param value * @param days */ - createCookie : function (name,value,days) { - if (days) - { + createCookie: function (name, value, days) { + if (days) { var date = new Date(); - date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); var expires = "; expires=" + date.toGMTString(); - } - else - { + } else { var expires = ""; } - document.cookie = name + "=" + value + expires + "; path=/; samesite=Lax"; + document.cookie = + name + "=" + value + expires + "; path=/; samesite=Lax"; }, - readCookie : function (name) { + readCookie: function (name) { var nameEQ = name + "="; - var ca = document.cookie.split(';'); + var ca = document.cookie.split(";"); - for (var i = 0; i < ca.length; i++) - { + for (var i = 0; i < ca.length; i++) { var c = ca[i]; - while (c.charAt(0) == ' ') - { - c = c.substring(1,c.length); + while (c.charAt(0) == " ") { + c = c.substring(1, c.length); } - if (c.indexOf(nameEQ) == 0) - { - return c.substring(nameEQ.length,c.length); + if (c.indexOf(nameEQ) == 0) { + return c.substring(nameEQ.length, c.length); } } return null; }, trimSlash: function (text) { - return text.replace(/^\/|\/$/g, ''); + return text.replace(/^\/|\/$/g, ""); }, routerLink: function () { var row, _location; - var rowGet = this.toolbar.querySelectorAll('td[data-debugbar-route="GET"]'); - var patt = /\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)/; + var rowGet = this.toolbar.querySelectorAll( + 'td[data-debugbar-route="GET"]' + ); + var patt = /\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)/; - for (var i = 0; i < rowGet.length; i++) - { + for (var i = 0; i < rowGet.length; i++) { row = rowGet[i]; - if (!/\/\(.+?\)/.test(rowGet[i].innerText)) - { - row.style = 'cursor: pointer;'; - row.setAttribute('title', location.origin + '/' + ciDebugBar.trimSlash(row.innerText)); - row.addEventListener('click', function (ev) { - _location = location.origin + '/' + ciDebugBar.trimSlash(ev.target.innerText); - var redirectWindow = window.open(_location, '_blank'); + if (!/\/\(.+?\)/.test(rowGet[i].innerText)) { + row.style = "cursor: pointer;"; + row.setAttribute( + "title", + location.origin + "/" + ciDebugBar.trimSlash(row.innerText) + ); + row.addEventListener("click", function (ev) { + _location = + location.origin + + "/" + + ciDebugBar.trimSlash(ev.target.innerText); + var redirectWindow = window.open(_location, "_blank"); redirectWindow.location; }); - } - else - { - row.innerHTML = '
' + row.innerText + '
' - + '
' - + row.innerText.replace(patt, '') - + '' - + '
'; + } else { + row.innerHTML = + "
" + + row.innerText + + "
" + + '
' + + row.innerText.replace( + patt, + '' + ) + + '' + + "
"; } } - rowGet = this.toolbar.querySelectorAll('td[data-debugbar-route="GET"] form'); - for (var i = 0; i < rowGet.length; i++) - { + rowGet = this.toolbar.querySelectorAll( + 'td[data-debugbar-route="GET"] form' + ); + for (var i = 0; i < rowGet.length; i++) { row = rowGet[i]; - row.addEventListener('submit', function (event) { - event.preventDefault() - var inputArray = [], t = 0; - var input = event.target.querySelectorAll('input[type=text]'); - var tpl = event.target.getAttribute('data-debugbar-route-tpl'); + row.addEventListener("submit", function (event) { + event.preventDefault(); + var inputArray = [], + t = 0; + var input = event.target.querySelectorAll("input[type=text]"); + var tpl = event.target.getAttribute("data-debugbar-route-tpl"); - for (var n = 0; n < input.length; n++) - { - if (input[n].value.length > 0) - { + for (var n = 0; n < input.length; n++) { + if (input[n].value.length > 0) { inputArray.push(input[n].value); } } - if (inputArray.length > 0) - { - _location = location.origin + '/' + tpl.replace(/\?/g, function () { - return inputArray[t++] - }); + if (inputArray.length > 0) { + _location = + location.origin + + "/" + + tpl.replace(/\?/g, function () { + return inputArray[t++]; + }); - var redirectWindow = window.open(_location, '_blank'); + var redirectWindow = window.open(_location, "_blank"); redirectWindow.location; } - }) + }); } - } + }, }; diff --git a/system/Debug/Toolbar/Views/toolbar.tpl.php b/system/Debug/Toolbar/Views/toolbar.tpl.php index 7c0d5336a5d4..30526726ca00 100644 --- a/system/Debug/Toolbar/Views/toolbar.tpl.php +++ b/system/Debug/Toolbar/Views/toolbar.tpl.php @@ -34,6 +34,11 @@
🔅 + + + + + diff --git a/system/HotReloader/DirectoryHasher.php b/system/HotReloader/DirectoryHasher.php new file mode 100644 index 000000000000..80df77aacec0 --- /dev/null +++ b/system/HotReloader/DirectoryHasher.php @@ -0,0 +1,61 @@ +hashApp())); + } + + /** + * Generates an array of md5 hashes for all directories that are + * watched by the Hot Reloader, as defined in the Config\Toolbar. + */ + public function hashApp(): array + { + $hashes = []; + + $watchedDirectories = config('Toolbar')->watchedDirectories; + + foreach ($watchedDirectories as $directory) { + if (is_dir(ROOTPATH . $directory)) { + $hashes[] = $this->hashDirectory(ROOTPATH . $directory); + } + } + + return array_unique(array_filter($hashes)); + } + + /** + * Generates an md5 hash of a given directory and all of its files + * that match the watched extensions defined in Config\Toolbar. + */ + public function hashDirectory(string $path): string + { + $directory = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS); + $filter = new IteratorFilter($directory); + $iterator = new RecursiveIteratorIterator($filter); + + $hashes = []; + + foreach ($iterator as $file) { + if ($file->isFile()) { + $hashes[] = md5_file($file->getRealPath()); + } + } + + return md5(implode('', $hashes)); + } +} diff --git a/system/HotReloader/HotReloader.php b/system/HotReloader/HotReloader.php new file mode 100644 index 000000000000..14efd297a39b --- /dev/null +++ b/system/HotReloader/HotReloader.php @@ -0,0 +1,52 @@ +hash(); + + while (true) { + if( connection_status() != CONNECTION_NORMAL or connection_aborted() ) { + break; + } + + $currentHash = $hasher->hash(); + + // If hash has changed, tell the browser to reload. + if ($currentHash !== $appHash) { + $appHash = $currentHash; + + $this->sendEvent('reload', ['time' => date('Y-m-d H:i:s')]); + break; + } + + sleep(1); + } + } + + /** + * Send an event to the browser. + */ + private function sendEvent(string $event, array $data): void + { + echo "event: {$event}\n"; + echo "data: " . json_encode($data) ."\n\n"; + + ob_flush(); + flush(); + } +} diff --git a/system/HotReloader/IteratorFilter.php b/system/HotReloader/IteratorFilter.php new file mode 100644 index 000000000000..903f7256f2af --- /dev/null +++ b/system/HotReloader/IteratorFilter.php @@ -0,0 +1,39 @@ +watchedExtensions = config('Toolbar')->watchedExtensions; + } + + /** + * Apply filters to the files in the iterator. + */ + public function accept(): bool + { + if (! $this->current()->isFile()) { + return true; + } + + $filename = $this->current()->getFilename(); + + // Skip hidden files and directories. + if ($filename[0] === '.') { + return false; + } + + // Only consume files of interest. + $ext = trim(strtolower($this->current()->getExtension()), '. '); + return in_array($ext, $this->watchedExtensions); + } +} diff --git a/user_guide_src/source/changelogs/v4.4.0.rst b/user_guide_src/source/changelogs/v4.4.0.rst index a90187940cf1..432e5e12ae20 100644 --- a/user_guide_src/source/changelogs/v4.4.0.rst +++ b/user_guide_src/source/changelogs/v4.4.0.rst @@ -56,6 +56,8 @@ Method Signature Changes Enhancements ************ +- The Debug Toolbar now has a new "Hot Reload" feature that can be used to automatically reload the page when a file is changed. See :ref:`debug-toolbar-hot-reload`. + Commands ======== diff --git a/user_guide_src/source/testing/debugging.rst b/user_guide_src/source/testing/debugging.rst index b8da022d018f..84ebe8ce5950 100644 --- a/user_guide_src/source/testing/debugging.rst +++ b/user_guide_src/source/testing/debugging.rst @@ -172,3 +172,14 @@ The ``getVarData()`` method should return an array containing arrays of key/valu outer array's key is the name of the section on the Vars tab: .. literalinclude:: debugging/006.php + +Hot Reloading +============= + +The Debug Toolbar includes a feature called Hot Reloading that allows you to make changes to your application's code and have them automatically reloaded in the browser without having to refresh the page. This is a great time-saver during development. + +To enable Hot Reloading while you are developing, you can click the button on the left side of the toolbar that looks like a refresh icon. This will enable Hot Reloading for all pages until you disable it. + +Hot Reloading works by scanning the files within the ``app`` directory every second and looking for changes. If it finds any, it will send a message to the browser to reload the page. It does not scan any other directories, so if you are making changes to files outside of the ``app`` directory, you will need to manually refresh the page. + +If you need to watch files outside of the ``app`` directory, or are finding it slow due to the size of your project, you can specify the directories to scan and the file extensions to scan for in the ``$toolbarRefreshDirs`` and ``$toolbarRefreshExtensions`` properties of the **app/Config/Toolbar.php** configuration file. From 3840990d1df9dfdb2b2732ea04b038fa254d9eb1 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Sun, 14 May 2023 23:32:48 -0500 Subject: [PATCH 02/20] Keep nginx from sending 504 time-out errors Co-authored-by: Michal Sniatala --- system/HotReloader/HotReloader.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system/HotReloader/HotReloader.php b/system/HotReloader/HotReloader.php index 14efd297a39b..ab40dfef734e 100644 --- a/system/HotReloader/HotReloader.php +++ b/system/HotReloader/HotReloader.php @@ -32,6 +32,8 @@ public function run() $this->sendEvent('reload', ['time' => date('Y-m-d H:i:s')]); break; + } elseif (rand(1, 10) > 8) { + $this->sendEvent('ping', ['time' => date('Y-m-d H:i:s')]); } sleep(1); From a22a8c1697cec27b458673f28d3eeee6792086ab Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Sun, 14 May 2023 23:51:55 -0500 Subject: [PATCH 03/20] fix: Keep scrollbar from showing up due to rotating icon --- system/Debug/Toolbar/Views/toolbar.tpl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Debug/Toolbar/Views/toolbar.tpl.php b/system/Debug/Toolbar/Views/toolbar.tpl.php index 30526726ca00..8a978d63800c 100644 --- a/system/Debug/Toolbar/Views/toolbar.tpl.php +++ b/system/Debug/Toolbar/Views/toolbar.tpl.php @@ -34,7 +34,7 @@
🔅 - + From 8be1d5f3f62775df41860da643a2317450df1169 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Sun, 14 May 2023 23:52:09 -0500 Subject: [PATCH 04/20] fix: Remove output_buffering call that cannot actually be set with ini_set --- system/HotReloader/HotReloader.php | 1 - 1 file changed, 1 deletion(-) diff --git a/system/HotReloader/HotReloader.php b/system/HotReloader/HotReloader.php index ab40dfef734e..fd02e3f16199 100644 --- a/system/HotReloader/HotReloader.php +++ b/system/HotReloader/HotReloader.php @@ -6,7 +6,6 @@ class HotReloader { public function run() { - ini_set('output_buffering', 'Off'); ini_set('zlib.output_compression', 'Off'); header("Cache-Control: no-store"); From caeaf5df7f3e2213df40f9b596d705471eef6ec3 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Sun, 14 May 2023 23:56:01 -0500 Subject: [PATCH 05/20] fix: Make the hot reload script work for subfolder hosting. --- system/Debug/Toolbar/Views/toolbar.js | 2 +- system/Debug/Toolbar/Views/toolbar.tpl.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/system/Debug/Toolbar/Views/toolbar.js b/system/Debug/Toolbar/Views/toolbar.js index bfff574507e7..928dc947f768 100644 --- a/system/Debug/Toolbar/Views/toolbar.js +++ b/system/Debug/Toolbar/Views/toolbar.js @@ -650,7 +650,7 @@ var ciDebugBar = { }, hotReloadConnect: function () { - const eventSource = new EventSource("/__hot-reload"); + const eventSource = new EventSource(ciSiteURL + "/__hot-reload"); eventSource.addEventListener("reload", function (e) { console.log("reload", e); diff --git a/system/Debug/Toolbar/Views/toolbar.tpl.php b/system/Debug/Toolbar/Views/toolbar.tpl.php index 8a978d63800c..3bc3df133b69 100644 --- a/system/Debug/Toolbar/Views/toolbar.tpl.php +++ b/system/Debug/Toolbar/Views/toolbar.tpl.php @@ -23,6 +23,7 @@
From aa800a99d5e74e8f6a7d718b0b7026a6d10a977a Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Mon, 22 May 2023 22:25:55 -0500 Subject: [PATCH 06/20] Add tests for Directory Hasher. --- system/Exceptions/FrameworkException.php | 5 ++ system/HotReloader/DirectoryHasher.php | 7 ++- system/Language/en/Core.php | 1 + .../HotReloader/DirectoryHasherTest.php | 58 +++++++++++++++++++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 tests/system/HotReloader/DirectoryHasherTest.php diff --git a/system/Exceptions/FrameworkException.php b/system/Exceptions/FrameworkException.php index 333f999df84a..cee5f903e785 100644 --- a/system/Exceptions/FrameworkException.php +++ b/system/Exceptions/FrameworkException.php @@ -33,6 +33,11 @@ public static function forInvalidFile(string $path) return new static(lang('Core.invalidFile', [$path])); } + public static function forInvalidDirectory(string $path) + { + return new static(lang('Core.invalidDirectory', [$path])); + } + public static function forCopyError(string $path) { return new static(lang('Core.copyError', [$path])); diff --git a/system/HotReloader/DirectoryHasher.php b/system/HotReloader/DirectoryHasher.php index 80df77aacec0..7658ece8fdf3 100644 --- a/system/HotReloader/DirectoryHasher.php +++ b/system/HotReloader/DirectoryHasher.php @@ -2,6 +2,7 @@ namespace CodeIgniter\HotReloader; +use CodeIgniter\Exceptions\FrameworkException; use FilesystemIterator; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; @@ -31,7 +32,7 @@ public function hashApp(): array foreach ($watchedDirectories as $directory) { if (is_dir(ROOTPATH . $directory)) { - $hashes[] = $this->hashDirectory(ROOTPATH . $directory); + $hashes[$directory] = $this->hashDirectory(ROOTPATH . $directory); } } @@ -44,6 +45,10 @@ public function hashApp(): array */ public function hashDirectory(string $path): string { + if (! is_dir($path)) { + throw FrameworkException::forInvalidDirectory($path); + } + $directory = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS); $filter = new IteratorFilter($directory); $iterator = new RecursiveIteratorIterator($filter); diff --git a/system/Language/en/Core.php b/system/Language/en/Core.php index 9ab549a4c8fe..1adfbe446560 100644 --- a/system/Language/en/Core.php +++ b/system/Language/en/Core.php @@ -14,6 +14,7 @@ 'copyError' => 'An error was encountered while attempting to replace the file "{0}". Please make sure your file directory is writable.', 'enabledZlibOutputCompression' => 'Your zlib.output_compression ini directive is turned on. This will not work well with output buffers.', 'invalidFile' => 'Invalid file: "{0}"', + 'invalidDirectory' => 'Directory does not exist: "{0}"', 'invalidPhpVersion' => 'Your PHP version must be {0} or higher to run CodeIgniter. Current version: {1}', 'missingExtension' => 'The framework needs the following extension(s) installed and loaded: "{0}".', 'noHandlers' => '"{0}" must provide at least one Handler.', diff --git a/tests/system/HotReloader/DirectoryHasherTest.php b/tests/system/HotReloader/DirectoryHasherTest.php new file mode 100644 index 000000000000..48c19cf4baba --- /dev/null +++ b/tests/system/HotReloader/DirectoryHasherTest.php @@ -0,0 +1,58 @@ +hasher = new DirectoryHasher(); + } + + public function testHashApp() + { + $results = $this->hasher->hashApp(); + + $this->assertIsArray($results); + $this->assertArrayHasKey('app', $results); + } + + public function testHashDirectoryInvalid() + { + $this->expectException(FrameworkException::class); + $this->expectExceptionMessage('Directory does not exist: "' . APPPATH . 'Foo"'); + + $this->hasher->hashDirectory(APPPATH . 'Foo'); + } + + public function testUniqueHashes() + { + $hash1 = $this->hasher->hashDirectory(APPPATH); + $hash2 = $this->hasher->hashDirectory(SYSTEMPATH); + + $this->assertNotEquals($hash1, $hash2); + } + + public function testRepeatableHashes() + { + $hash1 = $this->hasher->hashDirectory(APPPATH); + $hash2 = $this->hasher->hashDirectory(APPPATH); + + $this->assertEquals($hash1, $hash2); + } + + public function testHash() + { + $expected = md5(implode('', $this->hasher->hashApp())); + + $this->assertEquals($expected, $this->hasher->hash()); + } +} From 470bcb9234499a47b45c4a63b64164b06a39f9e7 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Mon, 22 May 2023 22:30:25 -0500 Subject: [PATCH 07/20] Added michalsn's suggestion to rotate the image not the anchor. --- system/Debug/Toolbar/Views/toolbar.js | 2 +- system/Debug/Toolbar/Views/toolbar.tpl.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/system/Debug/Toolbar/Views/toolbar.js b/system/Debug/Toolbar/Views/toolbar.js index 928dc947f768..073a76ab4312 100644 --- a/system/Debug/Toolbar/Views/toolbar.js +++ b/system/Debug/Toolbar/Views/toolbar.js @@ -613,7 +613,7 @@ var ciDebugBar = { setHotReloadState: function () { var btn = document.getElementById("debug-hot-reload").parentNode; - var btnImg = btn.firstElementChild; + var btnImg = btn.getElementsByTagName("img")[0]; var eventSource; // If the Hot Reload Collector is inactive stops here diff --git a/system/Debug/Toolbar/Views/toolbar.tpl.php b/system/Debug/Toolbar/Views/toolbar.tpl.php index 3bc3df133b69..6e62340c6ded 100644 --- a/system/Debug/Toolbar/Views/toolbar.tpl.php +++ b/system/Debug/Toolbar/Views/toolbar.tpl.php @@ -35,7 +35,7 @@
🔅 - + From d2c8e455ee701d13f996b431850a1affbd28636e Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Mon, 22 May 2023 22:43:48 -0500 Subject: [PATCH 08/20] fix: attempting to fix style, user guide, and other small errors --- user_guide_src/source/testing/debugging.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/user_guide_src/source/testing/debugging.rst b/user_guide_src/source/testing/debugging.rst index 84ebe8ce5950..138bf49af2bf 100644 --- a/user_guide_src/source/testing/debugging.rst +++ b/user_guide_src/source/testing/debugging.rst @@ -173,6 +173,8 @@ outer array's key is the name of the section on the Vars tab: .. literalinclude:: debugging/006.php +.. _debug-toolbar-hot-reload: + Hot Reloading ============= From c6b6341207ac038bd98bb20473bee0027d3864d7 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Mon, 22 May 2023 22:44:12 -0500 Subject: [PATCH 09/20] fix: attempting to fix style, user guide, and other small errors --- app/Config/Events.php | 5 +++-- app/Config/Toolbar.php | 2 +- system/HotReloader/DirectoryHasher.php | 3 +++ system/HotReloader/HotReloader.php | 3 +++ system/HotReloader/IteratorFilter.php | 5 ++++- 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/Config/Events.php b/app/Config/Events.php index bb48c66efde8..e3cebdd3d2bd 100644 --- a/app/Config/Events.php +++ b/app/Config/Events.php @@ -4,6 +4,7 @@ use CodeIgniter\Events\Events; use CodeIgniter\Exceptions\FrameworkException; +use CodeIgniter\HotReloader\HotReloader; /* * -------------------------------------------------------------------- @@ -46,8 +47,8 @@ Services::toolbar()->respond(); // Hot Reload route - for framework use on the hot reloader. if (ENVIRONMENT === 'development') { - Services::routes()->get('__hot-reload', function() { - (new \CodeIgniter\HotReloader\HotReloader())->run(); + Services::routes()->get('__hot-reload', static function() { + (new HotReloader())->run(); }); } } diff --git a/app/Config/Toolbar.php b/app/Config/Toolbar.php index 38a42b59f1cf..97fbda281287 100644 --- a/app/Config/Toolbar.php +++ b/app/Config/Toolbar.php @@ -113,6 +113,6 @@ class Toolbar extends BaseConfig * used to determine if the hot-reload feature should reload the page or not. */ public array $watchedExtensions = [ - 'php', 'css', 'js', 'html', 'svg', 'json', 'env' + 'php', 'css', 'js', 'html', 'svg', 'json', 'env', ]; } diff --git a/system/HotReloader/DirectoryHasher.php b/system/HotReloader/DirectoryHasher.php index 7658ece8fdf3..b6acab63c311 100644 --- a/system/HotReloader/DirectoryHasher.php +++ b/system/HotReloader/DirectoryHasher.php @@ -7,6 +7,9 @@ use RecursiveDirectoryIterator; use RecursiveIteratorIterator; +/** + * @internal + */ class DirectoryHasher { /** diff --git a/system/HotReloader/HotReloader.php b/system/HotReloader/HotReloader.php index fd02e3f16199..3834042e4ce0 100644 --- a/system/HotReloader/HotReloader.php +++ b/system/HotReloader/HotReloader.php @@ -2,6 +2,9 @@ namespace CodeIgniter\HotReloader; +/** + * @internal + */ class HotReloader { public function run() diff --git a/system/HotReloader/IteratorFilter.php b/system/HotReloader/IteratorFilter.php index 903f7256f2af..bef1d20e4c31 100644 --- a/system/HotReloader/IteratorFilter.php +++ b/system/HotReloader/IteratorFilter.php @@ -5,7 +5,10 @@ use RecursiveFilterIterator; use RecursiveIterator; -class IteratorFilter extends RecursiveFilterIterator +/** + * @internal + */ +class IteratorFilter extends RecursiveFilterIterator implements RecursiveIterator { private array $watchedExtensions = []; From 2b52354fa98fd7a44faad8706b061741a785e4cd Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Mon, 22 May 2023 22:46:46 -0500 Subject: [PATCH 10/20] fix: additional code style fix --- app/Config/Events.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Config/Events.php b/app/Config/Events.php index e3cebdd3d2bd..993abd24ebc7 100644 --- a/app/Config/Events.php +++ b/app/Config/Events.php @@ -47,7 +47,7 @@ Services::toolbar()->respond(); // Hot Reload route - for framework use on the hot reloader. if (ENVIRONMENT === 'development') { - Services::routes()->get('__hot-reload', static function() { + Services::routes()->get('__hot-reload', static function () { (new HotReloader())->run(); }); } From 4463200e0812c23eb9d75c715ddf7bd5c624fdf7 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Mon, 22 May 2023 23:15:57 -0500 Subject: [PATCH 11/20] More style fixes --- system/HotReloader/DirectoryHasher.php | 15 +++++++++--- system/HotReloader/HotReloader.php | 24 +++++++++++++------ system/HotReloader/IteratorFilter.php | 14 +++++++++-- .../HotReloader/DirectoryHasherTest.php | 22 +++++++++++++---- 4 files changed, 58 insertions(+), 17 deletions(-) diff --git a/system/HotReloader/DirectoryHasher.php b/system/HotReloader/DirectoryHasher.php index b6acab63c311..fac072fdb24a 100644 --- a/system/HotReloader/DirectoryHasher.php +++ b/system/HotReloader/DirectoryHasher.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + namespace CodeIgniter\HotReloader; use CodeIgniter\Exceptions\FrameworkException; @@ -10,7 +19,7 @@ /** * @internal */ -class DirectoryHasher +final class DirectoryHasher { /** * Generates an md5 value of all directories that are watched by the @@ -53,8 +62,8 @@ public function hashDirectory(string $path): string } $directory = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS); - $filter = new IteratorFilter($directory); - $iterator = new RecursiveIteratorIterator($filter); + $filter = new IteratorFilter($directory); + $iterator = new RecursiveIteratorIterator($filter); $hashes = []; diff --git a/system/HotReloader/HotReloader.php b/system/HotReloader/HotReloader.php index 3834042e4ce0..2f10fae6a566 100644 --- a/system/HotReloader/HotReloader.php +++ b/system/HotReloader/HotReloader.php @@ -1,28 +1,37 @@ + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + namespace CodeIgniter\HotReloader; /** * @internal */ -class HotReloader +final class HotReloader { public function run() { ini_set('zlib.output_compression', 'Off'); - header("Cache-Control: no-store"); - header("Content-Type: text/event-stream"); + header('Cache-Control: no-store'); + header('Content-Type: text/event-stream'); header('Access-Control-Allow-Methods: GET'); ob_end_clean(); set_time_limit(0); - $hasher = new DirectoryHasher(); + $hasher = new DirectoryHasher(); $appHash = $hasher->hash(); while (true) { - if( connection_status() != CONNECTION_NORMAL or connection_aborted() ) { + if (connection_status() !== CONNECTION_NORMAL || connection_aborted()) { break; } @@ -34,7 +43,8 @@ public function run() $this->sendEvent('reload', ['time' => date('Y-m-d H:i:s')]); break; - } elseif (rand(1, 10) > 8) { + } + if (mt_rand(1, 10) > 8) { $this->sendEvent('ping', ['time' => date('Y-m-d H:i:s')]); } @@ -48,7 +58,7 @@ public function run() private function sendEvent(string $event, array $data): void { echo "event: {$event}\n"; - echo "data: " . json_encode($data) ."\n\n"; + echo 'data: ' . json_encode($data) . "\n\n"; ob_flush(); flush(); diff --git a/system/HotReloader/IteratorFilter.php b/system/HotReloader/IteratorFilter.php index bef1d20e4c31..f3ce7aecf9ef 100644 --- a/system/HotReloader/IteratorFilter.php +++ b/system/HotReloader/IteratorFilter.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + namespace CodeIgniter\HotReloader; use RecursiveFilterIterator; @@ -8,7 +17,7 @@ /** * @internal */ -class IteratorFilter extends RecursiveFilterIterator implements RecursiveIterator +final class IteratorFilter extends RecursiveFilterIterator implements RecursiveIterator { private array $watchedExtensions = []; @@ -37,6 +46,7 @@ public function accept(): bool // Only consume files of interest. $ext = trim(strtolower($this->current()->getExtension()), '. '); - return in_array($ext, $this->watchedExtensions); + + return in_array($ext, $this->watchedExtensions, true); } } diff --git a/tests/system/HotReloader/DirectoryHasherTest.php b/tests/system/HotReloader/DirectoryHasherTest.php index 48c19cf4baba..a6113443ce33 100644 --- a/tests/system/HotReloader/DirectoryHasherTest.php +++ b/tests/system/HotReloader/DirectoryHasherTest.php @@ -1,16 +1,28 @@ + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + namespace Tests\System\HotReloader; use CodeIgniter\Exceptions\FrameworkException; use CodeIgniter\HotReloader\DirectoryHasher; use CodeIgniter\Test\CIUnitTestCase; -class DirectoryHasherTest extends CIUnitTestCase +/** + * @internal + */ +final class DirectoryHasherTest extends CIUnitTestCase { protected DirectoryHasher $hasher; - public function setUp(): void + protected function setUp(): void { parent::setUp(); @@ -38,7 +50,7 @@ public function testUniqueHashes() $hash1 = $this->hasher->hashDirectory(APPPATH); $hash2 = $this->hasher->hashDirectory(SYSTEMPATH); - $this->assertNotEquals($hash1, $hash2); + $this->assertNotSame($hash1, $hash2); } public function testRepeatableHashes() @@ -46,13 +58,13 @@ public function testRepeatableHashes() $hash1 = $this->hasher->hashDirectory(APPPATH); $hash2 = $this->hasher->hashDirectory(APPPATH); - $this->assertEquals($hash1, $hash2); + $this->assertSame($hash1, $hash2); } public function testHash() { $expected = md5(implode('', $this->hasher->hashApp())); - $this->assertEquals($expected, $this->hasher->hash()); + $this->assertSame($expected, $this->hasher->hash()); } } From 884a91e3dabd867c74c7fc87c9490ff0d64a9ce8 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Mon, 5 Jun 2023 22:29:13 -0500 Subject: [PATCH 12/20] Rector updates --- tests/system/HotReloader/DirectoryHasherTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/system/HotReloader/DirectoryHasherTest.php b/tests/system/HotReloader/DirectoryHasherTest.php index a6113443ce33..c10bae8fa7c0 100644 --- a/tests/system/HotReloader/DirectoryHasherTest.php +++ b/tests/system/HotReloader/DirectoryHasherTest.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace Tests\System\HotReloader; +namespace CodeIgniter\HotReloader; use CodeIgniter\Exceptions\FrameworkException; use CodeIgniter\HotReloader\DirectoryHasher; @@ -20,7 +20,7 @@ */ final class DirectoryHasherTest extends CIUnitTestCase { - protected DirectoryHasher $hasher; + private DirectoryHasher $hasher; protected function setUp(): void { From 0bca6467f7149be6cc876dc9f183933fe676ea06 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Fri, 23 Jun 2023 23:34:27 -0500 Subject: [PATCH 13/20] Run cs-fix --- tests/system/HotReloader/DirectoryHasherTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/system/HotReloader/DirectoryHasherTest.php b/tests/system/HotReloader/DirectoryHasherTest.php index c10bae8fa7c0..08f2ea6eef2a 100644 --- a/tests/system/HotReloader/DirectoryHasherTest.php +++ b/tests/system/HotReloader/DirectoryHasherTest.php @@ -12,7 +12,6 @@ namespace CodeIgniter\HotReloader; use CodeIgniter\Exceptions\FrameworkException; -use CodeIgniter\HotReloader\DirectoryHasher; use CodeIgniter\Test\CIUnitTestCase; /** From 3fd7e4ba8d9fd119e327e4d098de197adf23a1f2 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Fri, 23 Jun 2023 23:47:04 -0500 Subject: [PATCH 14/20] CSS corrections, and other utility fixes --- admin/css/debug-toolbar/toolbar.scss | 12 +++++++++++- system/Debug/Toolbar/Views/toolbar.css | 1 + system/HotReloader/IteratorFilter.php | 3 +++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/admin/css/debug-toolbar/toolbar.scss b/admin/css/debug-toolbar/toolbar.scss index 23e0807fabfc..01dfd2207b15 100644 --- a/admin/css/debug-toolbar/toolbar.scss +++ b/admin/css/debug-toolbar/toolbar.scss @@ -198,7 +198,7 @@ overflow: hidden; overflow-y: auto; padding: 0 12px 0 12px; - + // Give room for OS X scrollbar white-space: nowrap; z-index: 10000; @@ -501,3 +501,13 @@ .debug-bar-noverflow { overflow: hidden; } + +/* ENDLESS ROTATE */ +.rotate { + animation: rotate 9s linear infinite; +} +@keyframes rotate { + to { + transform: rotate(360deg); + } +} diff --git a/system/Debug/Toolbar/Views/toolbar.css b/system/Debug/Toolbar/Views/toolbar.css index b8954dacfde3..60c3216bfc9c 100644 --- a/system/Debug/Toolbar/Views/toolbar.css +++ b/system/Debug/Toolbar/Views/toolbar.css @@ -807,6 +807,7 @@ .rotate { animation: rotate 9s linear infinite; } + @keyframes rotate { to { transform: rotate(360deg); diff --git a/system/HotReloader/IteratorFilter.php b/system/HotReloader/IteratorFilter.php index f3ce7aecf9ef..b98a028d2443 100644 --- a/system/HotReloader/IteratorFilter.php +++ b/system/HotReloader/IteratorFilter.php @@ -16,6 +16,9 @@ /** * @internal + * + * @template-extends RecursiveFilterIterator + * @template-implements RecursiveIterator */ final class IteratorFilter extends RecursiveFilterIterator implements RecursiveIterator { From a1bbe2dbce26e0c0b4a20bf4c8604147884563ef Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Fri, 23 Jun 2023 23:53:39 -0500 Subject: [PATCH 15/20] Attempting to make psalm happy --- system/HotReloader/IteratorFilter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/HotReloader/IteratorFilter.php b/system/HotReloader/IteratorFilter.php index b98a028d2443..753cfdf8b1a9 100644 --- a/system/HotReloader/IteratorFilter.php +++ b/system/HotReloader/IteratorFilter.php @@ -17,8 +17,8 @@ /** * @internal * - * @template-extends RecursiveFilterIterator - * @template-implements RecursiveIterator + * @extends RecursiveFilterIterator + * @implements RecursiveIterator */ final class IteratorFilter extends RecursiveFilterIterator implements RecursiveIterator { From 7fd7cca6556f5cef3fa43edaa23c19c480afbb59 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Sat, 24 Jun 2023 23:51:41 -0500 Subject: [PATCH 16/20] Run sass converter to generate toolbar.css --- system/Debug/Toolbar/Views/toolbar.css | 878 ++++++++++++------------- 1 file changed, 436 insertions(+), 442 deletions(-) diff --git a/system/Debug/Toolbar/Views/toolbar.css b/system/Debug/Toolbar/Views/toolbar.css index 60c3216bfc9c..38bab087ae81 100644 --- a/system/Debug/Toolbar/Views/toolbar.css +++ b/system/Debug/Toolbar/Views/toolbar.css @@ -7,327 +7,321 @@ * file that was distributed with this source code. */ #debug-icon { - bottom: 0; - position: fixed; - right: 0; - z-index: 10000; - height: 36px; - width: 36px; - margin: 0px; - padding: 0px; - clear: both; - text-align: center; + bottom: 0; + position: fixed; + right: 0; + z-index: 10000; + height: 36px; + width: 36px; + margin: 0px; + padding: 0px; + clear: both; + text-align: center; } #debug-icon a svg { - margin: 8px; - max-width: 20px; - max-height: 20px; + margin: 8px; + max-width: 20px; + max-height: 20px; } #debug-icon.fixed-top { - bottom: auto; - top: 0; + bottom: auto; + top: 0; } #debug-icon .debug-bar-ndisplay { - display: none; + display: none; } #debug-bar { - bottom: 0; - left: 0; - position: fixed; - right: 0; - z-index: 10000; - height: 36px; - line-height: 36px; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, - sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; - font-size: 16px; - font-weight: 400; + bottom: 0; + left: 0; + position: fixed; + right: 0; + z-index: 10000; + height: 36px; + line-height: 36px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + font-size: 16px; + font-weight: 400; } #debug-bar h1 { - display: flex; - font-weight: normal; - margin: 0 0 0 auto; + display: flex; + font-weight: normal; + margin: 0 0 0 auto; } #debug-bar h1 svg { - width: 16px; - margin-right: 5px; + width: 16px; + margin-right: 5px; } #debug-bar h2 { - font-size: 16px; - margin: 0; - padding: 5px 0 10px 0; + font-size: 16px; + margin: 0; + padding: 5px 0 10px 0; } #debug-bar h2 span { - font-size: 13px; + font-size: 13px; } #debug-bar h3 { - font-size: 12px; - font-weight: 200; - margin: 0 0 0 10px; - padding: 0; - text-transform: uppercase; + font-size: 12px; + font-weight: 200; + margin: 0 0 0 10px; + padding: 0; + text-transform: uppercase; } #debug-bar p { - font-size: 12px; - margin: 0 0 0 15px; - padding: 0; + font-size: 12px; + margin: 0 0 0 15px; + padding: 0; } #debug-bar a { - text-decoration: none; + text-decoration: none; } #debug-bar a:hover { - text-decoration: underline; + text-decoration: underline; } #debug-bar button { - border: 1px solid; - border-radius: 4px; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - cursor: pointer; - line-height: 15px; + border: 1px solid; + border-radius: 4px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + cursor: pointer; + line-height: 15px; } #debug-bar button:hover { - text-decoration: underline; + text-decoration: underline; } #debug-bar table { - border-collapse: collapse; - font-size: 14px; - line-height: normal; - margin: 5px 10px 15px 10px; - width: calc(100% - 10px); + border-collapse: collapse; + font-size: 14px; + line-height: normal; + margin: 5px 10px 15px 10px; + width: calc(100% - 10px); } #debug-bar table strong { - font-weight: 500; + font-weight: 500; } #debug-bar table th { - display: table-cell; - font-weight: 600; - padding-bottom: 0.7em; - text-align: left; + display: table-cell; + font-weight: 600; + padding-bottom: 0.7em; + text-align: left; } #debug-bar table tr { - border: none; + border: none; } #debug-bar table td { - border: none; - display: table-cell; - margin: 0; - text-align: left; + border: none; + display: table-cell; + margin: 0; + text-align: left; } #debug-bar table td:first-child { - max-width: 20%; + max-width: 20%; } #debug-bar table td:first-child.narrow { - width: 7em; + width: 7em; } #debug-bar td[data-debugbar-route] form { - display: none; + display: none; } #debug-bar td[data-debugbar-route]:hover form { - display: block; + display: block; } #debug-bar td[data-debugbar-route]:hover > div { - display: none; + display: none; } -#debug-bar td[data-debugbar-route] input[type="text"] { - padding: 2px; +#debug-bar td[data-debugbar-route] input[type=text] { + padding: 2px; } #debug-bar .toolbar { - display: flex; - overflow: hidden; - overflow-y: auto; - padding: 0 12px 0 12px; - white-space: nowrap; - z-index: 10000; + display: flex; + overflow: hidden; + overflow-y: auto; + padding: 0 12px 0 12px; + white-space: nowrap; + z-index: 10000; } #debug-bar.fixed-top { - bottom: auto; - top: 0; + bottom: auto; + top: 0; } #debug-bar.fixed-top .tab { - bottom: auto; - top: 36px; + bottom: auto; + top: 36px; } #debug-bar #toolbar-position a, #debug-bar #toolbar-theme a { - padding: 0 6px; - display: inline-flex; - vertical-align: top; + padding: 0 6px; + display: inline-flex; + vertical-align: top; } #debug-bar #toolbar-position a:hover, #debug-bar #toolbar-theme a:hover { - text-decoration: none; + text-decoration: none; } #debug-bar #debug-bar-link { - display: flex; - padding: 6px; + display: flex; + padding: 6px; } #debug-bar .ci-label { - display: inline-flex; - font-size: 14px; + display: inline-flex; + font-size: 14px; } #debug-bar .ci-label:hover { - cursor: pointer; + cursor: pointer; } #debug-bar .ci-label a { - color: inherit; - display: flex; - letter-spacing: normal; - padding: 0 10px; - text-decoration: none; - align-items: center; + color: inherit; + display: flex; + letter-spacing: normal; + padding: 0 10px; + text-decoration: none; + align-items: center; } #debug-bar .ci-label img { - margin: 6px 3px 6px 0; - width: 16px !important; + margin: 6px 3px 6px 0; + width: 16px !important; } #debug-bar .ci-label .badge { - border-radius: 12px; - -moz-border-radius: 12px; - -webkit-border-radius: 12px; - display: inline-block; - font-size: 75%; - font-weight: bold; - line-height: 12px; - margin-left: 5px; - padding: 2px 5px; - text-align: center; - vertical-align: baseline; - white-space: nowrap; + border-radius: 12px; + -moz-border-radius: 12px; + -webkit-border-radius: 12px; + display: inline-block; + font-size: 75%; + font-weight: bold; + line-height: 12px; + margin-left: 5px; + padding: 2px 5px; + text-align: center; + vertical-align: baseline; + white-space: nowrap; } #debug-bar .tab { - bottom: 35px; - display: none; - left: 0; - max-height: 62%; - overflow: hidden; - overflow-y: auto; - padding: 1em 2em; - position: fixed; - right: 0; - z-index: 9999; + bottom: 35px; + display: none; + left: 0; + max-height: 62%; + overflow: hidden; + overflow-y: auto; + padding: 1em 2em; + position: fixed; + right: 0; + z-index: 9999; } #debug-bar .timeline { - margin-left: 0; - width: 100%; + margin-left: 0; + width: 100%; } #debug-bar .timeline th { - border-left: 1px solid; - font-size: 12px; - font-weight: 200; - padding: 5px 5px 10px 5px; - position: relative; - text-align: left; + border-left: 1px solid; + font-size: 12px; + font-weight: 200; + padding: 5px 5px 10px 5px; + position: relative; + text-align: left; } #debug-bar .timeline th:first-child { - border-left: 0; + border-left: 0; } #debug-bar .timeline td { - border-left: 1px solid; - padding: 5px; - position: relative; + border-left: 1px solid; + padding: 5px; + position: relative; } #debug-bar .timeline td:first-child { - border-left: 0; - max-width: none; + border-left: 0; + max-width: none; } #debug-bar .timeline td.child-container { - padding: 0px; + padding: 0px; } #debug-bar .timeline td.child-container .timeline { - margin: 0px; + margin: 0px; } -#debug-bar - .timeline - td.child-container - .timeline - td:first-child:not(.child-container) { - padding-left: calc(5px + 10px * var(--level)); +#debug-bar .timeline td.child-container .timeline td:first-child:not(.child-container) { + padding-left: calc(5px + 10px * var(--level)); } #debug-bar .timeline .timer { - border-radius: 4px; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - display: inline-block; - padding: 5px; - position: absolute; - top: 30%; + border-radius: 4px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + display: inline-block; + padding: 5px; + position: absolute; + top: 30%; } #debug-bar .timeline .timeline-parent { - cursor: pointer; + cursor: pointer; } #debug-bar .timeline .timeline-parent td:first-child nav { - background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMCAxNTAiPjxwYXRoIGQ9Ik02IDdoMThsLTkgMTV6bTAgMzBoMThsLTkgMTV6bTAgNDVoMThsLTktMTV6bTAgMzBoMThsLTktMTV6bTAgMTJsMTggMThtLTE4IDBsMTgtMTgiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNNiAxMjZsMTggMThtLTE4IDBsMTgtMTgiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSIjNTU1Ii8+PC9zdmc+") - no-repeat scroll 0 0/15px 75px transparent; - background-position: 0 25%; - display: inline-block; - height: 15px; - width: 15px; - margin-right: 3px; - vertical-align: middle; + background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMCAxNTAiPjxwYXRoIGQ9Ik02IDdoMThsLTkgMTV6bTAgMzBoMThsLTkgMTV6bTAgNDVoMThsLTktMTV6bTAgMzBoMThsLTktMTV6bTAgMTJsMTggMThtLTE4IDBsMTgtMTgiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNNiAxMjZsMTggMThtLTE4IDBsMTgtMTgiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSIjNTU1Ii8+PC9zdmc+") no-repeat scroll 0 0/15px 75px transparent; + background-position: 0 25%; + display: inline-block; + height: 15px; + width: 15px; + margin-right: 3px; + vertical-align: middle; } #debug-bar .timeline .timeline-parent-open { - background-color: #dfdfdf; + background-color: #DFDFDF; } #debug-bar .timeline .timeline-parent-open td:first-child nav { - background-position: 0 75%; + background-position: 0 75%; } #debug-bar .timeline .child-row:hover { - background: transparent; + background: transparent; } #debug-bar .route-params, #debug-bar .route-params-item { - vertical-align: top; + vertical-align: top; } #debug-bar .route-params td:first-child, #debug-bar .route-params-item td:first-child { - font-style: italic; - padding-left: 1em; - text-align: right; + font-style: italic; + padding-left: 1em; + text-align: right; } .debug-view.show-view { - border: 1px solid; - margin: 4px; + border: 1px solid; + margin: 4px; } .debug-view-path { - font-family: monospace; - font-size: 12px; - letter-spacing: normal; - min-height: 16px; - padding: 2px; - text-align: left; + font-family: monospace; + font-size: 12px; + letter-spacing: normal; + min-height: 16px; + padding: 2px; + text-align: left; } .show-view .debug-view-path { - display: block !important; + display: block !important; } @media screen and (max-width: 1024px) { - #debug-bar .ci-label img { - margin: unset; - } - .hide-sm { - display: none !important; - } + #debug-bar .ci-label img { + margin: unset; + } + .hide-sm { + display: none !important; + } } #debug-icon { - background-color: #ffffff; - box-shadow: 0 0 4px #dfdfdf; - -moz-box-shadow: 0 0 4px #dfdfdf; - -webkit-box-shadow: 0 0 4px #dfdfdf; + background-color: #FFFFFF; + box-shadow: 0 0 4px #DFDFDF; + -moz-box-shadow: 0 0 4px #DFDFDF; + -webkit-box-shadow: 0 0 4px #DFDFDF; } #debug-icon a:active, #debug-icon a:link, #debug-icon a:visited { - color: #dd8615; + color: #DD8615; } #debug-bar { - background-color: #ffffff; - color: #434343; + background-color: #FFFFFF; + color: #434343; } #debug-bar h1, #debug-bar h2, @@ -341,217 +335,217 @@ #debug-bar td, #debug-bar button, #debug-bar .toolbar { - background-color: transparent; - color: #434343; + background-color: transparent; + color: #434343; } #debug-bar button { - background-color: #ffffff; + background-color: #FFFFFF; } #debug-bar table strong { - color: #dd8615; + color: #DD8615; } #debug-bar table tbody tr:hover { - background-color: #dfdfdf; + background-color: #DFDFDF; } #debug-bar table tbody tr.current { - background-color: #fdc894; + background-color: #FDC894; } #debug-bar table tbody tr.current:hover td { - background-color: #dd4814; - color: #ffffff; + background-color: #DD4814; + color: #FFFFFF; } #debug-bar .toolbar { - background-color: #ffffff; - box-shadow: 0 0 4px #dfdfdf; - -moz-box-shadow: 0 0 4px #dfdfdf; - -webkit-box-shadow: 0 0 4px #dfdfdf; + background-color: #FFFFFF; + box-shadow: 0 0 4px #DFDFDF; + -moz-box-shadow: 0 0 4px #DFDFDF; + -webkit-box-shadow: 0 0 4px #DFDFDF; } #debug-bar .toolbar img { - filter: brightness(0) invert(0.4); + filter: brightness(0) invert(0.4); } #debug-bar.fixed-top .toolbar { - box-shadow: 0 0 4px #dfdfdf; - -moz-box-shadow: 0 0 4px #dfdfdf; - -webkit-box-shadow: 0 0 4px #dfdfdf; + box-shadow: 0 0 4px #DFDFDF; + -moz-box-shadow: 0 0 4px #DFDFDF; + -webkit-box-shadow: 0 0 4px #DFDFDF; } #debug-bar.fixed-top .tab { - box-shadow: 0 1px 4px #dfdfdf; - -moz-box-shadow: 0 1px 4px #dfdfdf; - -webkit-box-shadow: 0 1px 4px #dfdfdf; + box-shadow: 0 1px 4px #DFDFDF; + -moz-box-shadow: 0 1px 4px #DFDFDF; + -webkit-box-shadow: 0 1px 4px #DFDFDF; } #debug-bar .muted { - color: #434343; + color: #434343; } #debug-bar .muted td { - color: #dfdfdf; + color: #DFDFDF; } #debug-bar .muted:hover td { - color: #434343; + color: #434343; } #debug-bar #toolbar-position, #debug-bar #toolbar-theme { - filter: brightness(0) invert(0.6); + filter: brightness(0) invert(0.6); } #debug-bar .ci-label.active { - background-color: #dfdfdf; + background-color: #DFDFDF; } #debug-bar .ci-label:hover { - background-color: #dfdfdf; + background-color: #DFDFDF; } #debug-bar .ci-label .badge { - background-color: #dd4814; - color: #ffffff; + background-color: #DD4814; + color: #FFFFFF; } #debug-bar .tab { - background-color: #ffffff; - box-shadow: 0 -1px 4px #dfdfdf; - -moz-box-shadow: 0 -1px 4px #dfdfdf; - -webkit-box-shadow: 0 -1px 4px #dfdfdf; + background-color: #FFFFFF; + box-shadow: 0 -1px 4px #DFDFDF; + -moz-box-shadow: 0 -1px 4px #DFDFDF; + -webkit-box-shadow: 0 -1px 4px #DFDFDF; } #debug-bar .timeline th, #debug-bar .timeline td { - border-color: #dfdfdf; + border-color: #DFDFDF; } #debug-bar .timeline .timer { - background-color: #dd8615; + background-color: #DD8615; } .debug-view.show-view { - border-color: #dd8615; + border-color: #DD8615; } .debug-view-path { - background-color: #fdc894; - color: #434343; + background-color: #FDC894; + color: #434343; } @media (prefers-color-scheme: dark) { - #debug-icon { - background-color: #252525; - box-shadow: 0 0 4px #dfdfdf; - -moz-box-shadow: 0 0 4px #dfdfdf; - -webkit-box-shadow: 0 0 4px #dfdfdf; - } - #debug-icon a:active, - #debug-icon a:link, - #debug-icon a:visited { - color: #dd8615; - } - #debug-bar { - background-color: #252525; - color: #dfdfdf; - } - #debug-bar h1, - #debug-bar h2, - #debug-bar h3, - #debug-bar p, - #debug-bar a, - #debug-bar button, - #debug-bar table, - #debug-bar thead, - #debug-bar tr, - #debug-bar td, - #debug-bar button, - #debug-bar .toolbar { - background-color: transparent; - color: #dfdfdf; - } - #debug-bar button { - background-color: #252525; - } - #debug-bar table strong { - color: #dd8615; - } - #debug-bar table tbody tr:hover { - background-color: #434343; - } - #debug-bar table tbody tr.current { - background-color: #fdc894; - } - #debug-bar table tbody tr.current td { - color: #252525; - } - #debug-bar table tbody tr.current:hover td { - background-color: #dd4814; - color: #ffffff; - } - #debug-bar .toolbar { - background-color: #434343; - box-shadow: 0 0 4px #434343; - -moz-box-shadow: 0 0 4px #434343; - -webkit-box-shadow: 0 0 4px #434343; - } - #debug-bar .toolbar img { - filter: brightness(0) invert(1); - } - #debug-bar.fixed-top .toolbar { - box-shadow: 0 0 4px #434343; - -moz-box-shadow: 0 0 4px #434343; - -webkit-box-shadow: 0 0 4px #434343; - } - #debug-bar.fixed-top .tab { - box-shadow: 0 1px 4px #434343; - -moz-box-shadow: 0 1px 4px #434343; - -webkit-box-shadow: 0 1px 4px #434343; - } - #debug-bar .muted { - color: #dfdfdf; - } - #debug-bar .muted td { - color: #434343; - } - #debug-bar .muted:hover td { - color: #dfdfdf; - } - #debug-bar #toolbar-position, - #debug-bar #toolbar-theme { - filter: brightness(0) invert(0.6); - } - #debug-bar .ci-label.active { - background-color: #252525; - } - #debug-bar .ci-label:hover { - background-color: #252525; - } - #debug-bar .ci-label .badge { - background-color: #dd4814; - color: #ffffff; - } - #debug-bar .tab { - background-color: #252525; - box-shadow: 0 -1px 4px #434343; - -moz-box-shadow: 0 -1px 4px #434343; - -webkit-box-shadow: 0 -1px 4px #434343; - } - #debug-bar .timeline th, - #debug-bar .timeline td { - border-color: #434343; - } - #debug-bar .timeline .timer { - background-color: #dd8615; - } - .debug-view.show-view { - border-color: #dd8615; - } - .debug-view-path { - background-color: #fdc894; - color: #434343; - } + #debug-icon { + background-color: #252525; + box-shadow: 0 0 4px #DFDFDF; + -moz-box-shadow: 0 0 4px #DFDFDF; + -webkit-box-shadow: 0 0 4px #DFDFDF; + } + #debug-icon a:active, + #debug-icon a:link, + #debug-icon a:visited { + color: #DD8615; + } + #debug-bar { + background-color: #252525; + color: #DFDFDF; + } + #debug-bar h1, + #debug-bar h2, + #debug-bar h3, + #debug-bar p, + #debug-bar a, + #debug-bar button, + #debug-bar table, + #debug-bar thead, + #debug-bar tr, + #debug-bar td, + #debug-bar button, + #debug-bar .toolbar { + background-color: transparent; + color: #DFDFDF; + } + #debug-bar button { + background-color: #252525; + } + #debug-bar table strong { + color: #DD8615; + } + #debug-bar table tbody tr:hover { + background-color: #434343; + } + #debug-bar table tbody tr.current { + background-color: #FDC894; + } + #debug-bar table tbody tr.current td { + color: #252525; + } + #debug-bar table tbody tr.current:hover td { + background-color: #DD4814; + color: #FFFFFF; + } + #debug-bar .toolbar { + background-color: #434343; + box-shadow: 0 0 4px #434343; + -moz-box-shadow: 0 0 4px #434343; + -webkit-box-shadow: 0 0 4px #434343; + } + #debug-bar .toolbar img { + filter: brightness(0) invert(1); + } + #debug-bar.fixed-top .toolbar { + box-shadow: 0 0 4px #434343; + -moz-box-shadow: 0 0 4px #434343; + -webkit-box-shadow: 0 0 4px #434343; + } + #debug-bar.fixed-top .tab { + box-shadow: 0 1px 4px #434343; + -moz-box-shadow: 0 1px 4px #434343; + -webkit-box-shadow: 0 1px 4px #434343; + } + #debug-bar .muted { + color: #DFDFDF; + } + #debug-bar .muted td { + color: #434343; + } + #debug-bar .muted:hover td { + color: #DFDFDF; + } + #debug-bar #toolbar-position, + #debug-bar #toolbar-theme { + filter: brightness(0) invert(0.6); + } + #debug-bar .ci-label.active { + background-color: #252525; + } + #debug-bar .ci-label:hover { + background-color: #252525; + } + #debug-bar .ci-label .badge { + background-color: #DD4814; + color: #FFFFFF; + } + #debug-bar .tab { + background-color: #252525; + box-shadow: 0 -1px 4px #434343; + -moz-box-shadow: 0 -1px 4px #434343; + -webkit-box-shadow: 0 -1px 4px #434343; + } + #debug-bar .timeline th, + #debug-bar .timeline td { + border-color: #434343; + } + #debug-bar .timeline .timer { + background-color: #DD8615; + } + .debug-view.show-view { + border-color: #DD8615; + } + .debug-view-path { + background-color: #FDC894; + color: #434343; + } } #toolbarContainer.dark #debug-icon { - background-color: #252525; - box-shadow: 0 0 4px #dfdfdf; - -moz-box-shadow: 0 0 4px #dfdfdf; - -webkit-box-shadow: 0 0 4px #dfdfdf; + background-color: #252525; + box-shadow: 0 0 4px #DFDFDF; + -moz-box-shadow: 0 0 4px #DFDFDF; + -webkit-box-shadow: 0 0 4px #DFDFDF; } #toolbarContainer.dark #debug-icon a:active, #toolbarContainer.dark #debug-icon a:link, #toolbarContainer.dark #debug-icon a:visited { - color: #dd8615; + color: #DD8615; } #toolbarContainer.dark #debug-bar { - background-color: #252525; - color: #dfdfdf; + background-color: #252525; + color: #DFDFDF; } #toolbarContainer.dark #debug-bar h1, #toolbarContainer.dark #debug-bar h2, @@ -565,109 +559,109 @@ #toolbarContainer.dark #debug-bar td, #toolbarContainer.dark #debug-bar button, #toolbarContainer.dark #debug-bar .toolbar { - background-color: transparent; - color: #dfdfdf; + background-color: transparent; + color: #DFDFDF; } #toolbarContainer.dark #debug-bar button { - background-color: #252525; + background-color: #252525; } #toolbarContainer.dark #debug-bar table strong { - color: #dd8615; + color: #DD8615; } #toolbarContainer.dark #debug-bar table tbody tr:hover { - background-color: #434343; + background-color: #434343; } #toolbarContainer.dark #debug-bar table tbody tr.current { - background-color: #fdc894; + background-color: #FDC894; } #toolbarContainer.dark #debug-bar table tbody tr.current td { - color: #252525; + color: #252525; } #toolbarContainer.dark #debug-bar table tbody tr.current:hover td { - background-color: #dd4814; - color: #ffffff; + background-color: #DD4814; + color: #FFFFFF; } #toolbarContainer.dark #debug-bar .toolbar { - background-color: #434343; - box-shadow: 0 0 4px #434343; - -moz-box-shadow: 0 0 4px #434343; - -webkit-box-shadow: 0 0 4px #434343; + background-color: #434343; + box-shadow: 0 0 4px #434343; + -moz-box-shadow: 0 0 4px #434343; + -webkit-box-shadow: 0 0 4px #434343; } #toolbarContainer.dark #debug-bar .toolbar img { - filter: brightness(0) invert(1); + filter: brightness(0) invert(1); } #toolbarContainer.dark #debug-bar.fixed-top .toolbar { - box-shadow: 0 0 4px #434343; - -moz-box-shadow: 0 0 4px #434343; - -webkit-box-shadow: 0 0 4px #434343; + box-shadow: 0 0 4px #434343; + -moz-box-shadow: 0 0 4px #434343; + -webkit-box-shadow: 0 0 4px #434343; } #toolbarContainer.dark #debug-bar.fixed-top .tab { - box-shadow: 0 1px 4px #434343; - -moz-box-shadow: 0 1px 4px #434343; - -webkit-box-shadow: 0 1px 4px #434343; + box-shadow: 0 1px 4px #434343; + -moz-box-shadow: 0 1px 4px #434343; + -webkit-box-shadow: 0 1px 4px #434343; } #toolbarContainer.dark #debug-bar .muted { - color: #dfdfdf; + color: #DFDFDF; } #toolbarContainer.dark #debug-bar .muted td { - color: #434343; + color: #434343; } #toolbarContainer.dark #debug-bar .muted:hover td { - color: #dfdfdf; + color: #DFDFDF; } #toolbarContainer.dark #debug-bar #toolbar-position, #toolbarContainer.dark #debug-bar #toolbar-theme { - filter: brightness(0) invert(0.6); + filter: brightness(0) invert(0.6); } #toolbarContainer.dark #debug-bar .ci-label.active { - background-color: #252525; + background-color: #252525; } #toolbarContainer.dark #debug-bar .ci-label:hover { - background-color: #252525; + background-color: #252525; } #toolbarContainer.dark #debug-bar .ci-label .badge { - background-color: #dd4814; - color: #ffffff; + background-color: #DD4814; + color: #FFFFFF; } #toolbarContainer.dark #debug-bar .tab { - background-color: #252525; - box-shadow: 0 -1px 4px #434343; - -moz-box-shadow: 0 -1px 4px #434343; - -webkit-box-shadow: 0 -1px 4px #434343; + background-color: #252525; + box-shadow: 0 -1px 4px #434343; + -moz-box-shadow: 0 -1px 4px #434343; + -webkit-box-shadow: 0 -1px 4px #434343; } #toolbarContainer.dark #debug-bar .timeline th, #toolbarContainer.dark #debug-bar .timeline td { - border-color: #434343; + border-color: #434343; } #toolbarContainer.dark #debug-bar .timeline .timer { - background-color: #dd8615; + background-color: #DD8615; } #toolbarContainer.dark .debug-view.show-view { - border-color: #dd8615; + border-color: #DD8615; } #toolbarContainer.dark .debug-view-path { - background-color: #fdc894; - color: #434343; + background-color: #FDC894; + color: #434343; } -#toolbarContainer.dark td[data-debugbar-route] input[type="text"] { - background: #000; - color: #fff; +#toolbarContainer.dark td[data-debugbar-route] input[type=text] { + background: #000; + color: #fff; } #toolbarContainer.light #debug-icon { - background-color: #ffffff; - box-shadow: 0 0 4px #dfdfdf; - -moz-box-shadow: 0 0 4px #dfdfdf; - -webkit-box-shadow: 0 0 4px #dfdfdf; + background-color: #FFFFFF; + box-shadow: 0 0 4px #DFDFDF; + -moz-box-shadow: 0 0 4px #DFDFDF; + -webkit-box-shadow: 0 0 4px #DFDFDF; } #toolbarContainer.light #debug-icon a:active, #toolbarContainer.light #debug-icon a:link, #toolbarContainer.light #debug-icon a:visited { - color: #dd8615; + color: #DD8615; } #toolbarContainer.light #debug-bar { - background-color: #ffffff; - color: #434343; + background-color: #FFFFFF; + color: #434343; } #toolbarContainer.light #debug-bar h1, #toolbarContainer.light #debug-bar h2, @@ -681,135 +675,135 @@ #toolbarContainer.light #debug-bar td, #toolbarContainer.light #debug-bar button, #toolbarContainer.light #debug-bar .toolbar { - background-color: transparent; - color: #434343; + background-color: transparent; + color: #434343; } #toolbarContainer.light #debug-bar button { - background-color: #ffffff; + background-color: #FFFFFF; } #toolbarContainer.light #debug-bar table strong { - color: #dd8615; + color: #DD8615; } #toolbarContainer.light #debug-bar table tbody tr:hover { - background-color: #dfdfdf; + background-color: #DFDFDF; } #toolbarContainer.light #debug-bar table tbody tr.current { - background-color: #fdc894; + background-color: #FDC894; } #toolbarContainer.light #debug-bar table tbody tr.current:hover td { - background-color: #dd4814; - color: #ffffff; + background-color: #DD4814; + color: #FFFFFF; } #toolbarContainer.light #debug-bar .toolbar { - background-color: #ffffff; - box-shadow: 0 0 4px #dfdfdf; - -moz-box-shadow: 0 0 4px #dfdfdf; - -webkit-box-shadow: 0 0 4px #dfdfdf; + background-color: #FFFFFF; + box-shadow: 0 0 4px #DFDFDF; + -moz-box-shadow: 0 0 4px #DFDFDF; + -webkit-box-shadow: 0 0 4px #DFDFDF; } #toolbarContainer.light #debug-bar .toolbar img { - filter: brightness(0) invert(0.4); + filter: brightness(0) invert(0.4); } #toolbarContainer.light #debug-bar.fixed-top .toolbar { - box-shadow: 0 0 4px #dfdfdf; - -moz-box-shadow: 0 0 4px #dfdfdf; - -webkit-box-shadow: 0 0 4px #dfdfdf; + box-shadow: 0 0 4px #DFDFDF; + -moz-box-shadow: 0 0 4px #DFDFDF; + -webkit-box-shadow: 0 0 4px #DFDFDF; } #toolbarContainer.light #debug-bar.fixed-top .tab { - box-shadow: 0 1px 4px #dfdfdf; - -moz-box-shadow: 0 1px 4px #dfdfdf; - -webkit-box-shadow: 0 1px 4px #dfdfdf; + box-shadow: 0 1px 4px #DFDFDF; + -moz-box-shadow: 0 1px 4px #DFDFDF; + -webkit-box-shadow: 0 1px 4px #DFDFDF; } #toolbarContainer.light #debug-bar .muted { - color: #434343; + color: #434343; } #toolbarContainer.light #debug-bar .muted td { - color: #dfdfdf; + color: #DFDFDF; } #toolbarContainer.light #debug-bar .muted:hover td { - color: #434343; + color: #434343; } #toolbarContainer.light #debug-bar #toolbar-position, #toolbarContainer.light #debug-bar #toolbar-theme { - filter: brightness(0) invert(0.6); + filter: brightness(0) invert(0.6); } #toolbarContainer.light #debug-bar .ci-label.active { - background-color: #dfdfdf; + background-color: #DFDFDF; } #toolbarContainer.light #debug-bar .ci-label:hover { - background-color: #dfdfdf; + background-color: #DFDFDF; } #toolbarContainer.light #debug-bar .ci-label .badge { - background-color: #dd4814; - color: #ffffff; + background-color: #DD4814; + color: #FFFFFF; } #toolbarContainer.light #debug-bar .tab { - background-color: #ffffff; - box-shadow: 0 -1px 4px #dfdfdf; - -moz-box-shadow: 0 -1px 4px #dfdfdf; - -webkit-box-shadow: 0 -1px 4px #dfdfdf; + background-color: #FFFFFF; + box-shadow: 0 -1px 4px #DFDFDF; + -moz-box-shadow: 0 -1px 4px #DFDFDF; + -webkit-box-shadow: 0 -1px 4px #DFDFDF; } #toolbarContainer.light #debug-bar .timeline th, #toolbarContainer.light #debug-bar .timeline td { - border-color: #dfdfdf; + border-color: #DFDFDF; } #toolbarContainer.light #debug-bar .timeline .timer { - background-color: #dd8615; + background-color: #DD8615; } #toolbarContainer.light .debug-view.show-view { - border-color: #dd8615; + border-color: #DD8615; } #toolbarContainer.light .debug-view-path { - background-color: #fdc894; - color: #434343; + background-color: #FDC894; + color: #434343; } .debug-bar-width30 { - width: 30%; + width: 30%; } .debug-bar-width10 { - width: 10%; + width: 10%; } .debug-bar-width70p { - width: 70px; + width: 70px; } .debug-bar-width190p { - width: 190px; + width: 190px; } .debug-bar-width20e { - width: 20em; + width: 20em; } .debug-bar-width6r { - width: 6rem; + width: 6rem; } .debug-bar-ndisplay { - display: none; + display: none; } .debug-bar-alignRight { - text-align: right; + text-align: right; } .debug-bar-alignLeft { - text-align: left; + text-align: left; } .debug-bar-noverflow { - overflow: hidden; + overflow: hidden; } /* ENDLESS ROTATE */ .rotate { - animation: rotate 9s linear infinite; + animation: rotate 9s linear infinite; } @keyframes rotate { - to { - transform: rotate(360deg); - } + to { + transform: rotate(360deg); + } } From b1a2e1db503d8808486b0528b31967968d06376b Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Sat, 24 Jun 2023 23:53:20 -0500 Subject: [PATCH 17/20] Suppress Psalm errors on IteratorFilter --- system/HotReloader/IteratorFilter.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/system/HotReloader/IteratorFilter.php b/system/HotReloader/IteratorFilter.php index 753cfdf8b1a9..7f40a464d0ac 100644 --- a/system/HotReloader/IteratorFilter.php +++ b/system/HotReloader/IteratorFilter.php @@ -17,8 +17,7 @@ /** * @internal * - * @extends RecursiveFilterIterator - * @implements RecursiveIterator + * @psalm-suppress MissingTemplateParam */ final class IteratorFilter extends RecursiveFilterIterator implements RecursiveIterator { From fc5bd0290b90ef916365b954dda152280b0f4843 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Sun, 25 Jun 2023 00:01:58 -0500 Subject: [PATCH 18/20] Add missing group to test --- tests/system/HotReloader/DirectoryHasherTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/system/HotReloader/DirectoryHasherTest.php b/tests/system/HotReloader/DirectoryHasherTest.php index 08f2ea6eef2a..f5de5da523d5 100644 --- a/tests/system/HotReloader/DirectoryHasherTest.php +++ b/tests/system/HotReloader/DirectoryHasherTest.php @@ -16,6 +16,8 @@ /** * @internal + * + * @group Others */ final class DirectoryHasherTest extends CIUnitTestCase { From ac761f0a12df8ae88675b530bb2cb429eecc3393 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Sun, 25 Jun 2023 23:34:19 -0500 Subject: [PATCH 19/20] Apply suggestions from code review Co-authored-by: kenjis --- user_guide_src/source/testing/debugging.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/testing/debugging.rst b/user_guide_src/source/testing/debugging.rst index 138bf49af2bf..6c589ead52aa 100644 --- a/user_guide_src/source/testing/debugging.rst +++ b/user_guide_src/source/testing/debugging.rst @@ -178,10 +178,12 @@ outer array's key is the name of the section on the Vars tab: Hot Reloading ============= +.. versionadded:: 4.4.0 + The Debug Toolbar includes a feature called Hot Reloading that allows you to make changes to your application's code and have them automatically reloaded in the browser without having to refresh the page. This is a great time-saver during development. To enable Hot Reloading while you are developing, you can click the button on the left side of the toolbar that looks like a refresh icon. This will enable Hot Reloading for all pages until you disable it. -Hot Reloading works by scanning the files within the ``app`` directory every second and looking for changes. If it finds any, it will send a message to the browser to reload the page. It does not scan any other directories, so if you are making changes to files outside of the ``app`` directory, you will need to manually refresh the page. +Hot Reloading works by scanning the files within the **app** directory every second and looking for changes. If it finds any, it will send a message to the browser to reload the page. It does not scan any other directories, so if you are making changes to files outside of the **app** directory, you will need to manually refresh the page. -If you need to watch files outside of the ``app`` directory, or are finding it slow due to the size of your project, you can specify the directories to scan and the file extensions to scan for in the ``$toolbarRefreshDirs`` and ``$toolbarRefreshExtensions`` properties of the **app/Config/Toolbar.php** configuration file. +If you need to watch files outside of the **app** directory, or are finding it slow due to the size of your project, you can specify the directories to scan and the file extensions to scan for in the ``$watchedDirectories`` and ``$watchedExtensions`` properties of the **app/Config/Toolbar.php** configuration file. From 3b67f49045d2f708942e73da0de557dc61c6e55f Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Sun, 25 Jun 2023 23:35:42 -0500 Subject: [PATCH 20/20] Categorize the changelog entry for hot reloading --- user_guide_src/source/changelogs/v4.4.0.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/changelogs/v4.4.0.rst b/user_guide_src/source/changelogs/v4.4.0.rst index 432e5e12ae20..a082ba483447 100644 --- a/user_guide_src/source/changelogs/v4.4.0.rst +++ b/user_guide_src/source/changelogs/v4.4.0.rst @@ -56,8 +56,6 @@ Method Signature Changes Enhancements ************ -- The Debug Toolbar now has a new "Hot Reload" feature that can be used to automatically reload the page when a file is changed. See :ref:`debug-toolbar-hot-reload`. - Commands ======== @@ -67,6 +65,8 @@ Commands Testing ======= +- The Debug Toolbar now has a new "Hot Reload" feature that can be used to automatically reload the page when a file is changed. See :ref:`debug-toolbar-hot-reload`. + Database ========