From 8e7024cba50b18eb88d4edfd904a61aad8c97f5f Mon Sep 17 00:00:00 2001 From: Jack Wilkinson Date: Mon, 10 Feb 2025 15:56:51 +0000 Subject: [PATCH 01/12] Added better log viewing --- .../controllers/eventlogs/_field_details.php | 231 ++++++++++++++++++ .../system/controllers/eventlogs/preview.php | 12 +- modules/system/models/eventlog/fields.yaml | 4 + 3 files changed, 241 insertions(+), 6 deletions(-) create mode 100644 modules/system/controllers/eventlogs/_field_details.php diff --git a/modules/system/controllers/eventlogs/_field_details.php b/modules/system/controllers/eventlogs/_field_details.php new file mode 100644 index 0000000000..660e4ccf35 --- /dev/null +++ b/modules/system/controllers/eventlogs/_field_details.php @@ -0,0 +1,231 @@ + '/\b(for|foreach|while|class|extends|implements|try|catch|finally|function|return|unset|static|public|protected|private|count|global|if|else|else if|intval|int|array)\b/', + 'bool' => '/(\bnull\b|\btrue\b|\bfalse\b)/', + 'string' => '/('\w*')/', + 'bracket' => '/(\(|\)|\[|\]|\{|\})/', + 'variable' => '/(\$[a-z]\w*)/', + 'operator' => '/( \! | \!\= | \!== | = | == | === | > | >= | < | <= | and | or )/', + ]; + + foreach ($transform as $label => $regex) { + $str = preg_replace($regex, '$1', $str); + } + + return $str; +} + +function makeSnippet(array $snippet, ?int $highlight = null): string +{ + return implode( + "\n", + array_reduce( + array_keys($snippet), + function (array $carry, $key) use ($snippet, $highlight) { + $line = $key + 1; + $carry[] = sprintf( + '
%s: %s
', + ($line === $highlight ? ' highlight' : ''), + $line, + phpHighlight(e($snippet[$key], true)) + ); + return $carry; + }, + [] + ) + ); +} + +function getOrderedExceptionList(array $value): array +{ + $exceptions = [$value]; + $current = $value; + while (isset($current['previous']) && $current['previous']) { + $current = $current['previous']; + $exceptions[] = $current; + } + + return array_reverse($exceptions); +} +?> + +
+
+ $exception): ?> +
+

+

+ +
+
+
+ + at line +
+
+
+ +
+
+
+
+ +
+ Stack Trace +
+ $frame): ?> +
+
+ # + in + at line + + with argument 1 ? 's' : '' ?>: (, ', $frame['arguments']) ?>) + + + In App + +
+
+
+ +
+
+
+ +
+
+
+ +
+ +
+ + diff --git a/modules/system/controllers/eventlogs/preview.php b/modules/system/controllers/eventlogs/preview.php index 37200b5db7..dc2785f6c8 100644 --- a/modules/system/controllers/eventlogs/preview.php +++ b/modules/system/controllers/eventlogs/preview.php @@ -26,6 +26,12 @@
formRenderPreview() ?> + +

+ + + +

@@ -33,9 +39,3 @@

fatalError)) ?>

- -

- - - -

diff --git a/modules/system/models/eventlog/fields.yaml b/modules/system/models/eventlog/fields.yaml index 1a9c11ee9a..b9f8d1fea1 100644 --- a/modules/system/models/eventlog/fields.yaml +++ b/modules/system/models/eventlog/fields.yaml @@ -9,3 +9,7 @@ fields: path: field_message containerAttributes: data-plugin: exception-beautifier + + details: + type: partial + path: field_details From 3c0f2f7058074b589ee17c7c21341f34372403ab Mon Sep 17 00:00:00 2001 From: Jack Wilkinson Date: Mon, 10 Feb 2025 16:11:01 +0000 Subject: [PATCH 02/12] Added fixes to ensure snippet-less frames do not cause issues --- .../controllers/eventlogs/_field_details.php | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/modules/system/controllers/eventlogs/_field_details.php b/modules/system/controllers/eventlogs/_field_details.php index 660e4ccf35..4d204b4c44 100644 --- a/modules/system/controllers/eventlogs/_field_details.php +++ b/modules/system/controllers/eventlogs/_field_details.php @@ -169,11 +169,13 @@ function getOrderedExceptionList(array $value): array at line -
-
- + +
+
+ +
-
+
@@ -193,11 +195,13 @@ function getOrderedExceptionList(array $value): array In App -
-
- + +
+
+ +
-
+
@@ -214,7 +218,7 @@ function getOrderedExceptionList(array $value): array (() => { document.querySelectorAll('.trace-frame').forEach((frame) => { frame.querySelector('.label').addEventListener('click', () => { - frame.querySelector('div.snippet-preview-container').classList.toggle('folded'); + frame.querySelector('div.snippet-preview-container')?.classList.toggle('folded'); }); }); window.addEventListener('load', () => { From a2d0edd2be86b835af3330a33abb82965b3b973f Mon Sep 17 00:00:00 2001 From: Jack Wilkinson Date: Tue, 11 Feb 2025 17:02:32 +0000 Subject: [PATCH 03/12] Added better highlighting --- .../controllers/eventlogs/_field_details.php | 68 +++++++++++++++---- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/modules/system/controllers/eventlogs/_field_details.php b/modules/system/controllers/eventlogs/_field_details.php index 4d204b4c44..e928e0c29c 100644 --- a/modules/system/controllers/eventlogs/_field_details.php +++ b/modules/system/controllers/eventlogs/_field_details.php @@ -3,24 +3,57 @@ return; } -function phpHighlight(string $str): string +/** + * Highlights a line of php code with php syntax highlighting + * + * @param string $str + * @return string + */ +function phpSyntaxHighlight(string $str): string { - $transform = [ - 'control' => '/\b(for|foreach|while|class|extends|implements|try|catch|finally|function|return|unset|static|public|protected|private|count|global|if|else|else if|intval|int|array)\b/', + $regexes = [ + 'control' => '/\b(for|foreach|while|class |extends|yield from|yield|echo|fn|implements|try|catch|finally|throw|new|instanceof|function|return|unset|static|public|protected|private|count|global|if|else|else if|intval|int|array)\b/', 'bool' => '/(\bnull\b|\btrue\b|\bfalse\b)/', - 'string' => '/('\w*')/', + 'string' => '/('[\w\\\\\/]*'|"[\w\\\\\/]*")/', + 'number' => [ + 'pattern' => '/(=\(\s)?(\d+)(?=(\s|;|,|\)|=))/', + 'replace' => '$2', + 'before' => fn ($s) => str_replace(''', '\'', $s), + 'after' => fn ($s) => str_replace('\'', ''', $s), + ], 'bracket' => '/(\(|\)|\[|\]|\{|\})/', 'variable' => '/(\$[a-z]\w*)/', - 'operator' => '/( \! | \!\= | \!== | = | == | === | > | >= | < | <= | and | or )/', ]; - foreach ($transform as $label => $regex) { - $str = preg_replace($regex, '$1', $str); + if (preg_match('/(^\s*?\*|^\s*?\*\/|^\s*?\/\*|^\s*?\/\/|^\s*?#)/', $str)) { + return sprintf('%s', $str); + } + + foreach ($regexes as $label => $regex) { + if (is_string($regex)) { + $str = preg_replace($regex, '$1', $str); + continue; + } + + $str = preg_replace( + $regex['pattern'], + sprintf('%s', $label, $regex['replace'] ?? '$1'), + isset($regex['before']) ? $regex['before']($str) : $str + ); + + $str = isset($regex['after']) ? $regex['after']($str) : $str; } return $str; } +/** + * Converts an array of lines into a html snippet of code + * + * @param array $snippet + * @param int|null $highlight + * @return string + */ function makeSnippet(array $snippet, ?int $highlight = null): string { return implode( @@ -28,12 +61,11 @@ function makeSnippet(array $snippet, ?int $highlight = null): string array_reduce( array_keys($snippet), function (array $carry, $key) use ($snippet, $highlight) { - $line = $key + 1; $carry[] = sprintf( '
%s: %s
', - ($line === $highlight ? ' highlight' : ''), - $line, - phpHighlight(e($snippet[$key], true)) + ($key + 1 === $highlight ? ' highlight' : ''), + $key + 1, + phpSyntaxHighlight(e($snippet[$key], true)) ); return $carry; }, @@ -42,6 +74,12 @@ function (array $carry, $key) use ($snippet, $highlight) { ); } +/** + * Gets all exceptions in the stack and returns them bottom up + * + * @param array $value + * @return array + */ function getOrderedExceptionList(array $value): array { $exceptions = [$value]; @@ -107,10 +145,12 @@ function getOrderedExceptionList(array $value): array } div.snippet-preview span.bracket { color: #343434; } div.snippet-preview span.variable { color: #d3542f; } - div.snippet-preview span.operator { color: #055c9b; } - div.snippet-preview span.control { color: #8e09e1; } + div.snippet-preview span.control { color: #7109e1; } div.snippet-preview span.string { color: #6a8d00; } - div.snippet-preview span.bool { color: #096ee1; } + div.snippet-preview span.number { color: #006ac0; } + div.snippet-preview span.html { color: #cba604; } + div.snippet-preview span.bool { color: #e1095c; } + div.snippet-preview span.comment { color: #8c8c8c; } .trace-title { margin: 15px auto; display: block; From f367b01f28f0b8a7fb3827cea132ed0b2b9d01d7 Mon Sep 17 00:00:00 2001 From: Jack Wilkinson Date: Tue, 11 Feb 2025 17:20:52 +0000 Subject: [PATCH 04/12] Added better string handling and improved in app label --- .../system/controllers/eventlogs/_field_details.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/modules/system/controllers/eventlogs/_field_details.php b/modules/system/controllers/eventlogs/_field_details.php index e928e0c29c..e1c3cf0aa9 100644 --- a/modules/system/controllers/eventlogs/_field_details.php +++ b/modules/system/controllers/eventlogs/_field_details.php @@ -14,7 +14,11 @@ function phpSyntaxHighlight(string $str): string $regexes = [ 'control' => '/\b(for|foreach|while|class |extends|yield from|yield|echo|fn|implements|try|catch|finally|throw|new|instanceof|function|return|unset|static|public|protected|private|count|global|if|else|else if|intval|int|array)\b/', 'bool' => '/(\bnull\b|\btrue\b|\bfalse\b)/', - 'string' => '/('[\w\\\\\/]*'|"[\w\\\\\/]*")/', + 'string' => [ + 'pattern' => '/(\066[^\066]*\066|\065[^\065]*\065)/', + 'before' => fn ($s) => str_replace(''', "\066", str_replace('"', "\065", $s)), + 'after' => fn ($s) => str_replace("\066", ''', str_replace("\065", '"', $s)), + ], 'number' => [ 'pattern' => '/(=\(\s)?(\d+)(?=(\s|;|,|\)|=))/', 'replace' => '$2', @@ -186,11 +190,14 @@ function getOrderedExceptionList(array $value): array font-style: italic; } .trace-frame .label .app-icon{ - background: #a4e9ff; + background: #73b2d0; + color: #e9f3fa; border-radius: 6px; + font-size: 0.8em; padding: 3px; + font-weight: bold; float: right; - margin-top: -5px; + margin-top: -2px; } .trace-frame .folded { display: none; From f032cceab4587aec8dcefadfdaea079a1751e9d8 Mon Sep 17 00:00:00 2001 From: Jack Wilkinson Date: Wed, 12 Feb 2025 12:44:23 +0000 Subject: [PATCH 05/12] Added more detail and improved ui --- .../controllers/eventlogs/_field_details.php | 211 +++++++++++++----- 1 file changed, 161 insertions(+), 50 deletions(-) diff --git a/modules/system/controllers/eventlogs/_field_details.php b/modules/system/controllers/eventlogs/_field_details.php index e1c3cf0aa9..beb386f573 100644 --- a/modules/system/controllers/eventlogs/_field_details.php +++ b/modules/system/controllers/eventlogs/_field_details.php @@ -15,9 +15,9 @@ function phpSyntaxHighlight(string $str): string 'control' => '/\b(for|foreach|while|class |extends|yield from|yield|echo|fn|implements|try|catch|finally|throw|new|instanceof|function|return|unset|static|public|protected|private|count|global|if|else|else if|intval|int|array)\b/', 'bool' => '/(\bnull\b|\btrue\b|\bfalse\b)/', 'string' => [ - 'pattern' => '/(\066[^\066]*\066|\065[^\065]*\065)/', - 'before' => fn ($s) => str_replace(''', "\066", str_replace('"', "\065", $s)), - 'after' => fn ($s) => str_replace("\066", ''', str_replace("\065", '"', $s)), + 'pattern' => '/(\221[^\221]*\221|\222[^\222]*\222)/', + 'before' => fn ($s) => str_replace(''', "\221", str_replace('"', "\222", $s)), + 'after' => fn ($s) => str_replace("\221", ''', str_replace("\222", '"', $s)), ], 'number' => [ 'pattern' => '/(=\(\s)?(\d+)(?=(\s|;|,|\)|=))/', @@ -88,8 +88,7 @@ function getOrderedExceptionList(array $value): array { $exceptions = [$value]; $current = $value; - while (isset($current['previous']) && $current['previous']) { - $current = $current['previous']; + while (isset($current['previous']) && ($current = $current['previous'])) { $exceptions[] = $current; } @@ -100,11 +99,52 @@ function getOrderedExceptionList(array $value): array div.plugin-exception-beautifier span.beautifier-message-container { display: none; } + #winter-log-viewer { + background: #fff; + margin: -20px; + padding: 20px; + } #winter-log-viewer h1 { - margin-top: 0; + margin-top: 20px; + } + #winter-log-viewer .btn[disabled] { + color: #fff; + font-weight: bold; + user-select: auto; + } + #winter-log-viewer .btn.btn-secondary[disabled] { + color: #000; + font-weight: normal; + } + #winter-log-viewer table.table tr:first-child td, #winter-log-viewer table.table tr:first-child th { + border-top: 0; + } + #winter-log-viewer table.table tr td { + font-family: monospace; + } + #winter-log-viewer .input-group.select-container { + position: absolute; + right: 0; + } + #winter-log-viewer .input-group.select-container .select2-container--default { + width: auto; + } + #winter-log-viewer .input-group.select-container .select2-container--default .select2-selection { + padding-right: 30px; } - #winter-log-viewer .exception:not(:first-child) { - margin-top: 25px; + #winter-log-viewer .exception-list { + display: flex; + flex-direction: column; + width: 100%; + } + #winter-log-viewer .exception-list.reverse { + flex-direction: column-reverse; + } + #winter-log-viewer .exception-list .exception { + width: 100%; + } + #winter-log-viewer .btn-group:not(:last-of-type) { + margin-right: 5px; } p.message-log { font-family: monospace; @@ -158,8 +198,13 @@ function getOrderedExceptionList(array $value): array .trace-title { margin: 15px auto; display: block; + font-size: 1.2em; font-weight: bold; } + .trace-title small { + font-size: 0.85em; + font-weight: normal; + } .trace { border: 1px solid #dcdcdc; border-radius: 6px; @@ -184,6 +229,7 @@ function getOrderedExceptionList(array $value): array cursor: pointer; width: 100%; font-size: 0.95em; + word-break: break-word; } .trace-frame .label .item { font-weight: bold; @@ -205,59 +251,120 @@ function getOrderedExceptionList(array $value): array
- $exception): ?> -
-

-

+
+ + + + + + + + + + + + + + + + + + + + +
HTTP Method
Url
User Agent
Client IP
+ + +
+ + +
+
+ + +
+
+ + +
+ +
-
-
-
- - at line + +
+ +
+ +
+
+ $exception): ?> +
+

+

+ +
+
+ + +
+
+ +
- -
-
- -
-
-
-
-
- Stack Trace
- $frame): ?> -
-
- # - in - at line - - with argument 1 ? 's' : '' ?>: (, ', $frame['arguments']) ?>) - - - In App - +
+
+ + at line +
+ +
+
+ +
- -
-
- -
+ +
+
+ +
+ Stack Trace ( frames) +
+ $frame): ?> +
+
+ # + in + at line + + with argument 1 ? 's' : '' ?>: (, ', $frame['arguments']) ?>) + + + In App +
- -
- + +
+
+ +
+
+ +
+ +
-
- + +
@@ -277,6 +384,10 @@ function getOrderedExceptionList(array $value): array document.querySelector('#winter-log-viewer .formatted').style.display = "none"; document.querySelector('#winter-log-viewer .raw').style.display = "block"; }); + // jQuery to tie in with select2 + $("select#exception-sort-order").on('change', (e) => { + document.querySelector('#winter-log-viewer .exception-list').classList[e.target.value === 'old' ? 'remove' : 'add']('reverse'); + }); }); })(); From 056494d66028bdd82c68f3bd0f2b43cab1d1e6ea Mon Sep 17 00:00:00 2001 From: Jack Wilkinson Date: Wed, 12 Feb 2025 12:46:00 +0000 Subject: [PATCH 06/12] Added fix for context casing --- modules/system/controllers/eventlogs/_field_details.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/system/controllers/eventlogs/_field_details.php b/modules/system/controllers/eventlogs/_field_details.php index beb386f573..8b6852bc46 100644 --- a/modules/system/controllers/eventlogs/_field_details.php +++ b/modules/system/controllers/eventlogs/_field_details.php @@ -252,7 +252,7 @@ function getOrderedExceptionList(array $value): array
- + From 5a5acce65aeabcbd7e0a3821857fa4d626d0aa2d Mon Sep 17 00:00:00 2001 From: Jack Wilkinson <31214002+jaxwilko@users.noreply.github.com> Date: Wed, 12 Feb 2025 13:49:28 +0000 Subject: [PATCH 07/12] Added parent to control list --- modules/system/controllers/eventlogs/_field_details.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/system/controllers/eventlogs/_field_details.php b/modules/system/controllers/eventlogs/_field_details.php index 8b6852bc46..d0b1005533 100644 --- a/modules/system/controllers/eventlogs/_field_details.php +++ b/modules/system/controllers/eventlogs/_field_details.php @@ -12,7 +12,7 @@ function phpSyntaxHighlight(string $str): string { $regexes = [ - 'control' => '/\b(for|foreach|while|class |extends|yield from|yield|echo|fn|implements|try|catch|finally|throw|new|instanceof|function|return|unset|static|public|protected|private|count|global|if|else|else if|intval|int|array)\b/', + 'control' => '/\b(for|foreach|while|class |extends|yield from|yield|echo|fn|implements|try|catch|finally|throw|new|instanceof|parent|function|return|unset|static|public|protected|private|count|global|if|else|else if|intval|int|array)\b/', 'bool' => '/(\bnull\b|\btrue\b|\bfalse\b)/', 'string' => [ 'pattern' => '/(\221[^\221]*\221|\222[^\222]*\222)/', From af3559c82b04b0c2131aba4dedc60fa91b54f475 Mon Sep 17 00:00:00 2001 From: Jack Wilkinson Date: Wed, 12 Feb 2025 15:33:24 +0000 Subject: [PATCH 08/12] Added tailwind ui fixes --- modules/system/controllers/eventlogs/_field_details.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/system/controllers/eventlogs/_field_details.php b/modules/system/controllers/eventlogs/_field_details.php index d0b1005533..b3288c5701 100644 --- a/modules/system/controllers/eventlogs/_field_details.php +++ b/modules/system/controllers/eventlogs/_field_details.php @@ -248,6 +248,14 @@ function getOrderedExceptionList(array $value): array .trace-frame .folded { display: none; } + /* The following are fixes for the TailwindUI plugin */ + #winter-log-viewer hr { + margin-bottom: 20px; + margin-top: 20px; + } + #winter-log-viewer h1 { + font-size: 36px; + }
From 93b8b9f20bacdf719fad1b6ee537c6b4629e8b05 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 12 Feb 2025 15:50:32 -0600 Subject: [PATCH 09/12] Lint generated code files without eval --- modules/cms/classes/CodeParser.php | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/modules/cms/classes/CodeParser.php b/modules/cms/classes/CodeParser.php index 5bb672133a..3d022ad7a8 100644 --- a/modules/cms/classes/CodeParser.php +++ b/modules/cms/classes/CodeParser.php @@ -153,12 +153,16 @@ protected function rebuild($path) $fileContents .= trim($body).PHP_EOL; $fileContents .= '}'.PHP_EOL; - $this->validate($fileContents); - $this->makeDirectorySafe(dirname($path)); $this->writeContentSafe($path, $fileContents); + // Attempt to load the generated code file to ensure any errors are thrown + // before the file is cached + if (!class_exists($className)) { + require_once $path; + } + return $className; } @@ -289,15 +293,6 @@ protected function getCachedFileInfo() // Helpers // - /** - * Evaluates PHP content in order to detect syntax errors. - * The method handles PHP errors and throws exceptions. - */ - protected function validate($php) - { - eval('?>'.$php); - } - /** * Extracts the class name from a cache file * @return string From c14c4b94264c4b643fc5013956a1e5766e51ebee Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 12 Feb 2025 16:47:53 -0600 Subject: [PATCH 10/12] Add link opener JS (not working) @jaxwilko pls fix :) --- .../eventlogs/exception-beautifier.links.js | 3 +- .../controllers/eventlogs/_field_details.php | 107 ++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) diff --git a/modules/system/assets/js/eventlogs/exception-beautifier.links.js b/modules/system/assets/js/eventlogs/exception-beautifier.links.js index 3f948ad869..d24907223d 100644 --- a/modules/system/assets/js/eventlogs/exception-beautifier.links.js +++ b/modules/system/assets/js/eventlogs/exception-beautifier.links.js @@ -7,10 +7,11 @@ var ExceptionBeautifier = $.fn.exceptionBeautifier.Constructor ExceptionBeautifier.EDITORS = { + vscode: {scheme: 'vscode://file/%file:%line', name: 'VS Code (vscode://)'}, + phpstorm: {scheme: 'phpstorm://open?file=%file&line=%line', name: 'PhpStorm (phpstorm://)'}, subl: {scheme: 'subl://open?url=file://%file&line=%line', name: 'Sublime (subl://)'}, txmt: {scheme: 'txmt://open/?url=file://%file&line=%line', name: 'TextMate (txmt://)'}, mvim: {scheme: 'mvim://open/?url=file://%file&line=%line', name: 'MacVim (mvim://)'}, - phpstorm: {scheme: 'phpstorm://open?file=%file&line=%line', name: 'PhpStorm (phpstorm://)'}, editor: {scheme: 'editor://open/?file=%file&line=%line', name: 'Custom (editor://)'} } diff --git a/modules/system/controllers/eventlogs/_field_details.php b/modules/system/controllers/eventlogs/_field_details.php index b3288c5701..afcefe3e7b 100644 --- a/modules/system/controllers/eventlogs/_field_details.php +++ b/modules/system/controllers/eventlogs/_field_details.php @@ -377,6 +377,113 @@ function getOrderedExceptionList(array $value): array
From a2d2834064cc4eb2e6c67eef0ac8f18db7d51ea6 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 12 Feb 2025 23:07:56 -0600 Subject: [PATCH 12/12] Minor UI enhancements --- .../controllers/eventlogs/_field_details.php | 14 ++++++++++++-- modules/system/controllers/eventlogs/preview.php | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/modules/system/controllers/eventlogs/_field_details.php b/modules/system/controllers/eventlogs/_field_details.php index c81402f2f2..1b26ddd2df 100644 --- a/modules/system/controllers/eventlogs/_field_details.php +++ b/modules/system/controllers/eventlogs/_field_details.php @@ -66,7 +66,7 @@ function makeSnippet(array $snippet, string $file, ?int $highlight = null): stri array_keys($snippet), function (array $carry, $key) use ($snippet, $file, $highlight) { $carry[] = sprintf( - '
%3$d: %4$s
', + '
%3$d: %4$s
', ($key + 1 === $highlight ? ' highlight' : ''), urlencode(str_replace('\\', '/', $file)), $key + 1, @@ -187,6 +187,16 @@ function getOrderedExceptionList(array $value): array } div.snippet-preview div.preview-line span.line-number { cursor: pointer; + position: relative; + } + div.snippet-preview div.preview-line span.line-number .icon { + opacity: 0; + position: absolute; + left: calc(100% + 1em); + transition: opacity linear .2s; + } + div.snippet-preview div.preview-line:hover span.line-number .icon { + opacity: 1; } div.snippet-preview div.preview-line.highlight span.line-number { color: red; @@ -275,7 +285,7 @@ function getOrderedExceptionList(array $value): array
diff --git a/modules/system/controllers/eventlogs/preview.php b/modules/system/controllers/eventlogs/preview.php index dc2785f6c8..316229e37b 100644 --- a/modules/system/controllers/eventlogs/preview.php +++ b/modules/system/controllers/eventlogs/preview.php @@ -24,7 +24,7 @@ -
+
formRenderPreview() ?>

Url - +