Skip to content

Commit 1b4fd54

Browse files
avosalmonStyleCIBot
authored andcommitted
[12.x] Update local exception page (laravel#57036)
* Configure Vite with Tailwind CSS * Register new renderer template * Base layout with static content * Use dashed border * Add separator * Replace hard-coded values * Replace hard-coded query * Add empty state * Clean up section container * Clean up * Add section-container * Use section-container in topbar * Clean up * Extract query component * Add request-header component * Add request-body component * Add routing component * Add routing-parameter component * Add overview component * Add request-url component * Add header component * Add topbar component * Add trace component skeleton * Group exception frames * Add formatted-source and file-with-line components * Add frames * Add previous frame * Show code snippet without syntax highlight * Use exceptionAsMarkdown * Remove unused props * Assign snippet to a variable * Syntax highlight code snippet * Update phiki * Syntax highlight query, body, and route parameters * Update phiki * Use LineDecoration to highlight code snippet * Unescape highlighted text * Add syntax-highlight component * Handle files with less than 5 lines * Install Alpine.js * Add layout component * Add laravel-ascii component * Update method badge * Mark frame as main * Expand/collapse frames * Use current color in SVGs * Don't show callable when the frame doesn't have a class * Copy request URL to clipboard * Truncate long source * Add tooltip * wip: tooltip * Add side prop to tooltip * Add tooltip on request url * Prevent non-vendor frame from overflowing * Allow horizontal scroll on request body * Add tooltip on request headers * Add tooltip on database query * Hide JS-dependent elements until Alpine is loaded * Paginate queries * Add database icon * Add folder icons * Add copy icon * Add globe icon * Add alert icon * Copy exception as markdown * Add badge component * Add http-method component * Expand frames on clicking parent div * Open file in editor * Use pointer cursor on pagination buttons * Replace custom tooltip component with tippy.js * Upgrade phiki/phiki * Use dark-plus theme * Use dvh instead of screen * wip: light mode * Style gutter text * Only show the first 100 queries * light mode styling * Add light theme for syntax highlighter * Uppercase keys * Style copy as markdown button * Clean up * Add hover effect on laravel ascii logo * Add shadow * Fix tooltip position for queries * Tweak padding for mobile * Allow HTML in tooltip * Show frame arguments * Syntax highlight frame source * Add markdown template * Replace renderer directory * Simplify CSS rendering * Remove unused method * Revert unintended changes * Rename array key * Rename type to operator * Set max width for tooltip * Display vendor frames in two lines * Apply fixes from StyleCI * Add empty state for routing context * Only round top corners when frame is expanded * Update dot color for non-vendor frame * Update topbar height * Adjust padding around header section * Move up request url and have it overlap the separator line * Adjust spacing * Adjust spacing for mobile * Replace shadow-sm with shadow-xs * Apply bg-white without opacity in light mode * Adjust spacing --------- Co-authored-by: StyleCI Bot <[email protected]>
1 parent 92bea89 commit 1b4fd54

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+2113
-2279
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"monolog/monolog": "^3.0",
4141
"nesbot/carbon": "^3.8.4",
4242
"nunomaduro/termwind": "^2.0",
43+
"phiki/phiki": "v2.0.0",
4344
"psr/container": "^1.1.1|^2.0.1",
4445
"psr/log": "^1.0|^2.0|^3.0",
4546
"psr/simple-cache": "^1.0|^2.0|^3.0",

src/Illuminate/Foundation/Exceptions/Renderer/Exception.php

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,13 @@ public function class()
8787
}
8888

8989
/**
90-
* Get the first "non-vendor" frame index.
90+
* Get the exception code.
9191
*
92-
* @return int
92+
* @return int|string
9393
*/
94-
public function defaultFrame()
94+
public function code()
9595
{
96-
$key = array_search(false, array_map(function (Frame $frame) {
97-
return $frame->isFromVendor();
98-
}, $this->frames()->all()));
99-
100-
return $key === false ? 0 : $key;
96+
return $this->exception->getCode();
10197
}
10298

10399
/**
@@ -120,9 +116,50 @@ public function frames()
120116
array_shift($trace);
121117
}
122118

123-
return new Collection(array_map(
124-
fn (array $trace) => new Frame($this->exception, $classMap, $trace, $this->basePath), $trace,
125-
));
119+
$frames = [];
120+
$previousFrame = null;
121+
122+
foreach (array_reverse($trace) as $frameData) {
123+
$frame = new Frame($this->exception, $classMap, $frameData, $this->basePath, $previousFrame);
124+
$frames[] = $frame;
125+
$previousFrame = $frame;
126+
}
127+
128+
$frames = array_reverse($frames);
129+
130+
foreach ($frames as $frame) {
131+
if (! $frame->isFromVendor()) {
132+
$frame->markAsMain();
133+
break;
134+
}
135+
}
136+
137+
return new Collection($frames);
138+
}
139+
140+
/**
141+
* Get the exception's frames grouped by vendor status.
142+
*
143+
* @return array<int, array{is_vendor: bool, frames: array<int, Frame>}>
144+
*/
145+
public function frameGroups()
146+
{
147+
$groups = [];
148+
149+
foreach ($this->frames() as $frame) {
150+
$isVendor = $frame->isFromVendor();
151+
152+
if (empty($groups) || $groups[array_key_last($groups)]['is_vendor'] !== $isVendor) {
153+
$groups[] = [
154+
'is_vendor' => $isVendor,
155+
'frames' => [],
156+
];
157+
}
158+
159+
$groups[array_key_last($groups)]['frames'][] = $frame;
160+
}
161+
162+
return $groups;
126163
}
127164

128165
/**

src/Illuminate/Foundation/Exceptions/Renderer/Frame.php

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,36 @@ class Frame
3737
*/
3838
protected $basePath;
3939

40+
/**
41+
* The previous frame.
42+
*
43+
* @var \Illuminate\Foundation\Exceptions\Renderer\Frame|null
44+
*/
45+
protected $previous;
46+
47+
/**
48+
* Whether this frame is the main (first non-vendor) frame.
49+
*
50+
* @var bool
51+
*/
52+
protected $isMain = false;
53+
4054
/**
4155
* Create a new frame instance.
4256
*
4357
* @param \Symfony\Component\ErrorHandler\Exception\FlattenException $exception
4458
* @param array<string, string> $classMap
45-
* @param array{file: string, line: int, class?: string, type?: string, function?: string} $frame
59+
* @param array{file: string, line: int, class?: string, type?: string, function?: string, args?: array} $frame
4660
* @param string $basePath
61+
* @param \Illuminate\Foundation\Exceptions\Renderer\Frame|null $previous
4762
*/
48-
public function __construct(FlattenException $exception, array $classMap, array $frame, string $basePath)
63+
public function __construct(FlattenException $exception, array $classMap, array $frame, string $basePath, ?Frame $previous = null)
4964
{
5065
$this->exception = $exception;
5166
$this->classMap = $classMap;
5267
$this->frame = $frame;
5368
$this->basePath = $basePath;
69+
$this->previous = $previous;
5470
}
5571

5672
/**
@@ -95,7 +111,11 @@ public function class()
95111
*/
96112
public function file()
97113
{
98-
return str_replace($this->basePath.'/', '', $this->frame['file']);
114+
return match (true) {
115+
! isset($this->frame['file']) => '[internal function]',
116+
! is_string($this->frame['file']) => '[unknown file]',
117+
default => str_replace($this->basePath.'/', '', $this->frame['file']),
118+
};
99119
}
100120

101121
/**
@@ -114,6 +134,16 @@ public function line()
114134
return $this->frame['line'] > $maxLines ? 1 : $this->frame['line'];
115135
}
116136

137+
/**
138+
* Get the frame's function operator.
139+
*
140+
* @return '::'|'->'|''
141+
*/
142+
public function operator()
143+
{
144+
return $this->frame['type'];
145+
}
146+
117147
/**
118148
* Get the frame's function or method.
119149
*
@@ -127,6 +157,27 @@ public function callable()
127157
};
128158
}
129159

160+
/**
161+
* Get the frame's arguments.
162+
*
163+
* @return array
164+
*/
165+
public function args()
166+
{
167+
if (! isset($this->frame['args']) || ! is_array($this->frame['args']) || count($this->frame['args']) === 0) {
168+
return [];
169+
}
170+
171+
return array_map(function ($argument) {
172+
[$key, $value] = $argument;
173+
174+
return match ($key) {
175+
'object' => "{$key}({$value})",
176+
default => $key,
177+
};
178+
}, $this->frame['args']);
179+
}
180+
130181
/**
131182
* Get the frame's code snippet.
132183
*
@@ -157,4 +208,34 @@ public function isFromVendor()
157208
return ! str_starts_with($this->frame['file'], $this->basePath)
158209
|| str_starts_with($this->frame['file'], $this->basePath.'/vendor');
159210
}
211+
212+
/**
213+
* Get the previous frame.
214+
*
215+
* @return \Illuminate\Foundation\Exceptions\Renderer\Frame|null
216+
*/
217+
public function previous()
218+
{
219+
return $this->previous;
220+
}
221+
222+
/**
223+
* Mark this frame as the main frame.
224+
*
225+
* @return void
226+
*/
227+
public function markAsMain()
228+
{
229+
$this->isMain = true;
230+
}
231+
232+
/**
233+
* Determine if this is the main frame.
234+
*
235+
* @return bool
236+
*/
237+
public function isMain()
238+
{
239+
return $this->isMain;
240+
}
160241
}

src/Illuminate/Foundation/Exceptions/Renderer/Listener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public function queries()
5959
*/
6060
public function onQueryExecuted(QueryExecuted $event)
6161
{
62-
if (count($this->queries) === 100) {
62+
if (count($this->queries) === 101) {
6363
return;
6464
}
6565

src/Illuminate/Foundation/Exceptions/Renderer/Renderer.php

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use Illuminate\Contracts\View\Factory;
66
use Illuminate\Foundation\Exceptions\Renderer\Mappers\BladeMapper;
77
use Illuminate\Http\Request;
8-
use Illuminate\Support\Collection;
98
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
109
use Throwable;
1110

@@ -108,19 +107,7 @@ public function render(Request $request, Throwable $throwable)
108107
*/
109108
public static function css()
110109
{
111-
return (new Collection([
112-
['styles.css', []],
113-
['light-mode.css', ['data-theme' => 'light']],
114-
['dark-mode.css', ['data-theme' => 'dark']],
115-
]))->map(function ($fileAndAttributes) {
116-
[$filename, $attributes] = $fileAndAttributes;
117-
118-
return '<style '.(new Collection($attributes))->map(function ($value, $attribute) {
119-
return $attribute.'="'.$value.'"';
120-
})->implode(' ').'>'
121-
.file_get_contents(static::DIST.$filename)
122-
.'</style>';
123-
})->implode('');
110+
return '<style>'.file_get_contents(static::DIST.'styles.css').'</style>';
124111
}
125112

126113
/**
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
@props(['type' => 'default', 'variant' => 'soft'])
2+
3+
@php
4+
$baseClasses = 'inline-flex w-fit shrink-0 items-center justify-center gap-1 font-mono leading-3 uppercase transition-colors dark:border [&_svg]:size-2.5 h-6 min-w-5 rounded-md px-1.5 text-xs/none';
5+
6+
$types = [
7+
'default' => [
8+
'soft' => 'bg-black/8 text-neutral-900 dark:border-neutral-700 dark:bg-white/10 dark:text-neutral-100',
9+
'solid' => 'bg-neutral-600 text-neutral-100 dark:border-neutral-500 dark:bg-neutral-600',
10+
],
11+
'success' => [
12+
'soft' => 'bg-emerald-200 text-emerald-900 dark:border-emerald-600 dark:bg-emerald-900/70 dark:text-emerald-400',
13+
'solid' => 'bg-emerald-600 dark:border-emerald-500 dark:bg-emerald-600',
14+
],
15+
'primary' => [
16+
'soft' => 'bg-blue-100 text-blue-900 dark:border-blue-800 dark:bg-blue-950 dark:text-blue-300',
17+
'solid' => 'bg-blue-700 dark:border-blue-600 dark:bg-blue-700',
18+
],
19+
'error' => [
20+
'soft' => 'bg-rose-200 text-rose-900 dark:border-rose-900 dark:bg-rose-950 dark:text-rose-100 dark:[&_svg]:!text-white',
21+
'solid' => 'bg-rose-600 dark:border-rose-500 dark:bg-rose-600',
22+
],
23+
'alert' => [
24+
'soft' => 'bg-amber-200 text-amber-900 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-300',
25+
'solid' => 'bg-amber-600 dark:border-amber-500 dark:bg-amber-600',
26+
],
27+
'white' => [
28+
'soft' => 'bg-white text-neutral-900 dark:border-neutral-700 dark:bg-neutral-800 dark:text-neutral-100',
29+
'solid' => 'bg-black/10 text-neutral-900 dark:text-neutral-900 dark:bg-white',
30+
],
31+
];
32+
33+
$variants = [
34+
'soft' => '',
35+
'solid' => 'text-white dark:text-white [&_svg]:!text-white',
36+
];
37+
38+
$typeClasses = $types[$type][$variant] ?? $types['default']['soft'];
39+
$variantClasses = $variants[$variant] ?? $variants['soft'];
40+
41+
$classes = implode(' ', [$baseClasses, $typeClasses, $variantClasses]);
42+
43+
@endphp
44+
45+
<div {{ $attributes->merge(['class' => $classes]) }}>
46+
{{ $slot }}
47+
</div>

src/Illuminate/Foundation/resources/exceptions/renderer/components/card.blade.php

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)