├── .blade.format.json ├── .phpunit.cache └── test-results ├── .prettierignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── bootstrap └── app.php ├── composer.json ├── database ├── factories │ └── ModelFactory.php └── migrations │ └── create_filament_exceptions_table.php.stub ├── package.json ├── phpunit.xml.dist.bak ├── pint.json ├── postcss.config.cjs ├── resources ├── css │ └── filament-exceptions.css ├── dist │ ├── filament-exceptions.css │ └── filament-exceptions.js ├── lang │ ├── cs │ │ └── filament-exceptions.php │ ├── en │ │ └── filament-exceptions.php │ └── sk │ │ └── filament-exceptions.php └── views │ ├── .gitkeep │ ├── body.blade.php │ ├── components │ ├── code-preview.blade.php │ └── query-preview.blade.php │ ├── cookies.blade.php │ ├── exception.blade.php │ ├── headers.blade.php │ ├── query.blade.php │ └── view-exception.blade.php ├── src ├── Commands │ └── InstallCommand.php ├── Concerns │ ├── HasLabels.php │ ├── HasModelPruneInterval.php │ ├── HasNavigation.php │ ├── HasTabs.php │ └── HasTenantScope.php ├── Facades │ └── FilamentExceptions.php ├── FilamentExceptions.php ├── FilamentExceptionsPlugin.php ├── FilamentExceptionsServiceProvider.php ├── Models │ └── Exception.php ├── QueryRecorder │ ├── Query.php │ └── QueryRecorder.php ├── Resources │ ├── ExceptionResource.php │ └── ExceptionResource │ │ └── Pages │ │ ├── ListExceptions.php │ │ └── ViewException.php └── Trace │ ├── CodeBlock.php │ ├── Frame.php │ └── Parser.php └── tailwind.config.js /.blade.format.json: -------------------------------------------------------------------------------- 1 | { 2 | "useLaravelPint": true 3 | } -------------------------------------------------------------------------------- /.phpunit.cache/test-results: -------------------------------------------------------------------------------- 1 | {"version":"pest_2.36.0","defects":[],"times":{"P\\Tests\\ArchTest::__pest_evaluable_it_will_not_use_debugging_functions":0.138,"P\\Tests\\ExampleTest::__pest_evaluable_it_can_test":0.001}} -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.md 2 | **/.git 3 | **/.github 4 | **/dist 5 | **/vendor 6 | **/node_modules 7 | composer.lock 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `filament-exceptions` will be documented in this file. 4 | 5 | ## 3.0.0 - 2025-05-13 6 | 7 | ### What's Changed 8 | 9 | * 3.x Rewrite by @bezhanSalleh in https://github.com/bezhanSalleh/filament-exceptions/pull/49 10 | 11 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/2.1.2...3.0.0 12 | 13 | ## 2.1.2 - 2024-11-06 14 | 15 | ### What's Changed 16 | 17 | * Adding isScopedToTenant at ExceptionResource by @LingMyat in https://github.com/bezhanSalleh/filament-exceptions/pull/47 18 | * Add Custom Model Support & Fix Iterator Errors by @Fludem in https://github.com/bezhanSalleh/filament-exceptions/pull/46 19 | * Bump aglipanci/laravel-pint-action from 2.3.1 to 2.4 by @dependabot in https://github.com/bezhanSalleh/filament-exceptions/pull/53 20 | * Slovak translation by @hamrak in https://github.com/bezhanSalleh/filament-exceptions/pull/55 21 | * Bump dependabot/fetch-metadata from 1.6.0 to 2.2.0 by @dependabot in https://github.com/bezhanSalleh/filament-exceptions/pull/59 22 | 23 | ### New Contributors 24 | 25 | * @LingMyat made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/47 26 | * @Fludem made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/46 27 | * @hamrak made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/55 28 | 29 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/2.0.2...2.1.2 30 | 31 | ## 2.1.0 - 2024-01-24 32 | 33 | ### What's Changed 34 | 35 | * Adding isScopedToTenant at ExceptionResource by @LingMyat in https://github.com/bezhanSalleh/filament-exceptions/pull/47 36 | * Add Custom Model Support & Fix Iterator Errors by @Fludem in https://github.com/bezhanSalleh/filament-exceptions/pull/46 37 | 38 | ### New Contributors 39 | 40 | * @LingMyat made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/47 41 | * @Fludem made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/46 42 | 43 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/2.0.2...2.1.0 44 | 45 | ## 2.0.2 - 2024-01-09 46 | 47 | ### What's Changed 48 | 49 | * Bump aglipanci/laravel-pint-action from 2.3.0 to 2.3.1 by @dependabot in https://github.com/bezhanSalleh/filament-exceptions/pull/43 50 | * use @svg directive to load icons by @iRaziul in https://github.com/bezhanSalleh/filament-exceptions/pull/42 51 | 52 | ### New Contributors 53 | 54 | * @iRaziul made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/42 55 | 56 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/2.0.1...2.0.2 57 | 58 | ## 2.0.1 - 2023-10-17 59 | 60 | ### What's Changed 61 | 62 | - Update README.md to include Filament panel plugin registration by @vanhooff in https://github.com/bezhanSalleh/filament-exceptions/pull/37 63 | - Bump actions/checkout from 3 to 4 by @dependabot in https://github.com/bezhanSalleh/filament-exceptions/pull/38 64 | - Bump stefanzweifel/git-auto-commit-action from 4 to 5 by @dependabot in https://github.com/bezhanSalleh/filament-exceptions/pull/39 65 | 66 | ### New Contributors 67 | 68 | - @vanhooff made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/37 69 | 70 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/2.0.0...2.0.1 71 | 72 | ## 2.0.0 - 2023-08-01 73 | 74 | ### What's Changed 75 | 76 | - Filament v3 support 77 | - Bump aglipanci/laravel-pint-action from 2.1.0 to 2.2.0 by @dependabot in https://github.com/bezhanSalleh/filament-exceptions/pull/29 78 | - Bump dependabot/fetch-metadata from 1.3.6 to 1.4.0 by @dependabot in https://github.com/bezhanSalleh/filament-exceptions/pull/31 79 | - Bump aglipanci/laravel-pint-action from 2.2.0 to 2.3.0 by @dependabot in https://github.com/bezhanSalleh/filament-exceptions/pull/33 80 | - Bump dependabot/fetch-metadata from 1.4.0 to 1.5.1 by @dependabot in https://github.com/bezhanSalleh/filament-exceptions/pull/34 81 | - Bump dependabot/fetch-metadata from 1.5.1 to 1.6.0 by @dependabot in https://github.com/bezhanSalleh/filament-exceptions/pull/35 82 | - Fix typo by @martin-ro in https://github.com/bezhanSalleh/filament-exceptions/pull/30 83 | 84 | ### New Contributors 85 | 86 | - @martin-ro made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/30 87 | 88 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/1.1.2...2.0.0 89 | 90 | ## 1.1.2 - 2023-03-19 91 | 92 | ### What's Changed 93 | 94 | - Fix dependency issue in composer.json by @patrickcurl in https://github.com/bezhanSalleh/filament-exceptions/pull/28 95 | 96 | ### New Contributors 97 | 98 | - @patrickcurl made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/28 99 | 100 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/1.1.1...1.1.2 101 | 102 | ## 1.1.1 - 2023-03-05 103 | 104 | **What's Changed**: 105 | 106 | - fixed script loading issue 107 | 108 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/1.1.0...1.1.1 109 | 110 | ## 1.1.0 - 2023-03-05 111 | 112 | ### What's Changed 113 | 114 | - feature: Exceptions are now Mass Prunable by @bezhanSalleh in https://github.com/bezhanSalleh/filament-exceptions/pull/27 115 | - Fix/laravel ignition dep by @bezhanSalleh in https://github.com/bezhanSalleh/filament-exceptions/pull/25 116 | - Fix/message column data type by @bezhanSalleh in https://github.com/bezhanSalleh/filament-exceptions/pull/26 117 | - Bump dependabot/fetch-metadata from 1.3.5 to 1.3.6 by @dependabot in https://github.com/bezhanSalleh/filament-exceptions/pull/23 118 | - Bump aglipanci/laravel-pint-action from 1.0.0 to 2.1.0 by @dependabot in https://github.com/bezhanSalleh/filament-exceptions/pull/20 119 | - refactor config/translation, fix some minor stuff by @josefbehr in https://github.com/bezhanSalleh/filament-exceptions/pull/24 120 | 121 | ### New Contributors 122 | 123 | - @josefbehr made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/24 124 | 125 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/1.0.3...1.1.0 126 | 127 | ## 1.0.3 - 2022-11-16 128 | 129 | ### What's Changed 130 | 131 | - Update code-preview.blade.php by @damms005 in https://github.com/bezhanSalleh/filament-exceptions/pull/17 132 | - Update view-exception.blade.php by @damms005 in https://github.com/bezhanSalleh/filament-exceptions/pull/15 133 | 134 | ### New Contributors 135 | 136 | - @damms005 made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/17 137 | 138 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/1.0.2...1.0.3 139 | 140 | ## 1.0.2 - 2022-11-12 141 | 142 | ### What's Changed 143 | 144 | - Bump dependabot/fetch-metadata from 1.3.3 to 1.3.4 by @dependabot in https://github.com/bezhanSalleh/filament-exceptions/pull/10 145 | - Bump dependabot/fetch-metadata from 1.3.4 to 1.3.5 by @dependabot in https://github.com/bezhanSalleh/filament-exceptions/pull/12 146 | - fixed viewing by @bezhanSalleh in https://github.com/bezhanSalleh/filament-exceptions/pull/13 147 | - fixed migrations publishing issue 148 | 149 | ### New Contributors 150 | 151 | - @dependabot made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/10 152 | 153 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/1.0.1...1.0.2 154 | 155 | ## 1.0.1 - 2022-10-03 156 | 157 | ### What's Changed 158 | 159 | - `feature:`**`Localizations`** by @bezhanSalleh in https://github.com/bezhanSalleh/filament-exceptions/pull/9 160 | 161 | - - Added all possible localizations 162 | 163 | - 164 | - 165 | - 166 | - 167 | - 168 | - - added `config` file 169 | 170 | - 171 | - 172 | - 173 | - 174 | - 175 | - - updated `exceptions:install` command 176 | 177 | - 178 | - 179 | - 180 | - 181 | - 182 | - 183 | - Add ability to customize navigation item from config file by @aminetiyal in https://github.com/bezhanSalleh/filament-exceptions/pull/4 184 | 185 | - Lang en by @MarJose123 in https://github.com/bezhanSalleh/filament-exceptions/pull/7 186 | 187 | 188 | ### New Contributors 189 | 190 | - @aminetiyal made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/4 191 | - @MarJose123 made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/7 192 | 193 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/1.0.0...1.0.1 194 | 195 | ## 1.0.0 - 2022-09-20 196 | 197 | ### What's Changed 198 | 199 | - `feature:`**Queries Tab** by @bezhanSalleh in https://github.com/bezhanSalleh/filament-exceptions/pull/5 200 | 201 | ### New Contributors 202 | 203 | - @bezhanSalleh made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/5 204 | 205 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/0.0.6...1.0.0 206 | 207 | ## 0.0.6 - 2022-09-08 208 | 209 | **What's New 210 | 211 | - PHP 8.0 Support 212 | 213 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/0.0.5...0.0.6 214 | 215 | ## 0.0.5 - 2022-09-07 216 | 217 | ### What's Changed 218 | 219 | - fix readme by @shuvroroy in https://github.com/bezhanSalleh/filament-exceptions/pull/1 220 | - Update README.md by @pxlrbt in https://github.com/bezhanSalleh/filament-exceptions/pull/2 221 | - Little typo by @ralphjsmit in https://github.com/bezhanSalleh/filament-exceptions/pull/3 222 | 223 | ### New Contributors 224 | 225 | - @shuvroroy made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/1 226 | - @pxlrbt made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/2 227 | - @ralphjsmit made their first contribution in https://github.com/bezhanSalleh/filament-exceptions/pull/3 228 | 229 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/0.0.4...0.0.5 230 | 231 | ## 0.0.4 - 2022-09-07 232 | 233 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/0.0.3...0.0.4 234 | 235 | ## 0.0.3 - 2022-09-07 236 | 237 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/compare/0.0.2...0.0.3 238 | 239 | ## 0.0.2 - 2022-09-05 240 | 241 | - assets optimized 242 | 243 | **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/commits/0.0.2 244 | 245 | ## 0.0.1 - 2022-09-04 246 | 247 | - initial release 248 | - **Full Changelog**: https://github.com/bezhanSalleh/filament-exceptions/commits/0.0.1 249 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) bezhanSalleh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | filament-exceptions-art 3 | 4 | 5 |

6 | 7 | FILAMENT 8.x 8 | 9 | 10 | Packagist 11 | 12 | 13 | Tests Passing 14 | 15 | 16 | Code Style Passing 17 | 18 | 19 | 20 | Downloads 21 | 22 |

23 | 24 | # Exception Viewer 25 | 26 | A Simple & Beautiful Exception Viewer for FilamentPHP's Admin Panel 27 | 28 | * For FilamentPHP 2.x use version 1.x 29 | 30 | ## Installation 31 | 32 | 1. You can install the package via composer: 33 | 34 | ```bash 35 | composer require bezhansalleh/filament-exceptions 36 | ``` 37 | 38 | 2. Publish and run the migration via: 39 | ```bash 40 | php artisan exceptions:install 41 | ``` 42 | 43 | 3. Register the plugin for the Filament Panel 44 | 45 | ```php 46 | public function panel(Panel $panel): Panel 47 | { 48 | return $panel 49 | ->plugins([ 50 | \BezhanSalleh\FilamentExceptions\FilamentExceptionsPlugin::make() 51 | ]); 52 | } 53 | ``` 54 | 55 | 4. Activate the plugin by editing your App's Exception Handler as follow: 56 | - **Laravel 11.x+** 57 | Enable it in the `bootstrap/app.php` file 58 | ```php 59 | withExceptions(function (Exceptions $exceptions) { 68 | $exceptions->reportable(function (Exception|Throwable $e) { 69 | FilamentExceptions::report($e); 70 | }); 71 | }) 72 | ... 73 | ``` 74 | - **Laravel 10.x** 75 | ```php 76 | reportable(function (Throwable $e) { 89 | if ($this->shouldReport($e)) { 90 | FilamentExceptions::report($e); 91 | } 92 | }); 93 | 94 | ... 95 | } 96 | } 97 | ``` 98 | 99 | ### Configuration Options 100 | 101 | When registering the FilamentExceptions plugin, you can chain various methods to customize its behavior. Here are all available configuration options: 102 | 103 | #### Navigation 104 | ```php 105 | FilamentExceptionsPlugin::make() 106 | ->navigationBadge(bool | Closure $condition = true) 107 | ->navigationBadgeColor(string | array | Closure $color) 108 | ->navigationGroup(string | Closure | null $group) 109 | ->navigationParentItem(string | Closure | null $item) 110 | ->navigationIcon(string | Closure | null $icon) 111 | ->activeNavigationIcon(string | Closure | null $icon) 112 | ->navigationLabel(string | Closure | null $label) 113 | ->navigationSort(int | Closure | null $sort) 114 | ->registerNavigation(bool | Closure $shouldRegisterNavigation) 115 | ->subNavigationPosition(SubNavigationPosition | Closure $position) 116 | ``` 117 | 118 | #### Labels and Search 119 | ```php 120 | FilamentExceptionsPlugin::make() 121 | ->modelLabel(string | Closure | null $label) 122 | ->pluralModelLabel(string | Closure | null $label) 123 | ->titleCaseModelLabel(bool | Closure $condition = true) 124 | ->globallySearchable(bool | Closure $condition = true) 125 | ``` 126 | 127 | #### Tabs Labels and Icons 128 | ```php 129 | FilamentExceptionsPlugin::make() 130 | ->activeTab(int $tab) // 1 = Exception, 2 = Headers, 3 = Cookies, 4 = Body, 5 = Queries 131 | ->bodyTabIcon(string $icon) 132 | ->bodyTabLabel(string $label) 133 | ->cookiesTabIcon(string $icon) 134 | ->cookiesTabLabel(string $label) 135 | ->exceptionTabIcon(string $icon) 136 | ->exceptionTabLabel(string $label) 137 | ->headersTabIcon(string $icon) 138 | ->headersTabLabel(string $label) 139 | ->queriesTabIcon(string $icon) 140 | ->queriesTabLabel(string $label) 141 | ``` 142 | 143 | #### Mass Pruning Settings 144 | ```php 145 | FilamentExceptionsPlugin::make() 146 | ->modelPruneInterval(Carbon $interval) 147 | ``` 148 | > **Note** This requires laravel scheduler to be setup and configured in order to work. You can see how to do that here [Running The Scheduler](https://laravel.com/docs/10.x/scheduling#running-the-scheduler) 149 | 150 | #### Tenancy Configuration 151 | ```php 152 | FilamentExceptionsPlugin::make() 153 | ->scopeToTenant(bool | Closure $condition = true) 154 | ->tenantOwnershipRelationshipName(string | Closure | null $ownershipRelationshipName) 155 | ->tenantRelationshipName(string | Closure | null $relationshipName) 156 | ``` 157 | 158 | #### General Configuration 159 | ```php 160 | FilamentExceptionsPlugin::make() 161 | ->cluster(string | Closure | null $cluster) 162 | ->slug(string | Closure | null $slug) 163 | ``` 164 | 165 | Example usage: 166 | ```php 167 | return $panel 168 | ->plugins([ 169 | FilamentExceptionsPlugin::make() 170 | ->navigationLabel('Error Logs') 171 | ->navigationIcon('heroicon-o-bug-ant') 172 | ->navigationBadge() 173 | ->navigationGroup('System') 174 | ->modelPruneInterval(now()->subDays(7)) 175 | ]); 176 | ``` 177 | 178 | ### Custom Exception Model 179 | 1. Extend the base model as follow: 180 | ```php 181 | configure([ 16 | 'enables_package_discoveries' => true, 17 | ]) 18 | ->createApplication(); 19 | 20 | $app->register(LivewireServiceProvider::class); 21 | $app->register(FilamentServiceProvider::class); 22 | $app->register(SupportServiceProvider::class); 23 | 24 | return $app; 25 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bezhansalleh/filament-exceptions", 3 | "description": "A Simple & Beautiful Pluggable Exception Viewer for FilamentPHP's Admin Panel", 4 | "keywords": [ 5 | "bezhanSalleh", 6 | "laravel", 7 | "filament-exceptions", 8 | "filament-exception-viewer" 9 | ], 10 | "homepage": "https://github.com/bezhansalleh/filament-exceptions", 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Bezhan Salleh", 15 | "email": "bezhan_salleh@yahoo.com", 16 | "role": "Developer" 17 | } 18 | ], 19 | "require": { 20 | "php": "^8.1|^8.2", 21 | "filament/filament": "^3.2", 22 | "spatie/laravel-package-tools": "^1.15" 23 | }, 24 | "require-dev": { 25 | "laravel/pint": "^1.0", 26 | "nunomaduro/collision": "^7.9", 27 | "orchestra/testbench": "^8.0", 28 | "pestphp/pest": "^2.1", 29 | "pestphp/pest-plugin-laravel": "^2.0", 30 | "pestphp/pest-plugin-livewire": "^2.0", 31 | "spatie/laravel-ray": "^1.26" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "BezhanSalleh\\FilamentExceptions\\": "src", 36 | "BezhanSalleh\\FilamentExceptions\\Database\\Factories\\": "database/factories" 37 | } 38 | }, 39 | "autoload-dev": { 40 | "psr-4": { 41 | "BezhanSalleh\\FilamentExceptions\\Tests\\": "tests" 42 | } 43 | }, 44 | "scripts": { 45 | "post-autoload-dump": "@php ./vendor/bin/testbench package:discover --ansi", 46 | "test": "vendor/bin/pest", 47 | "test-coverage": "vendor/bin/pest --coverage", 48 | "format": "vendor/bin/pint" 49 | }, 50 | "config": { 51 | "sort-packages": true, 52 | "allow-plugins": { 53 | "pestphp/pest-plugin": true, 54 | "phpstan/extension-installer": true 55 | } 56 | }, 57 | "extra": { 58 | "laravel": { 59 | "providers": [ 60 | "BezhanSalleh\\FilamentExceptions\\FilamentExceptionsServiceProvider" 61 | ], 62 | "aliases": { 63 | "FilamentExceptions": "BezhanSalleh\\FilamentExceptions\\Facades\\FilamentExceptions" 64 | } 65 | } 66 | }, 67 | "minimum-stability": "dev", 68 | "prefer-stable": true 69 | } 70 | -------------------------------------------------------------------------------- /database/factories/ModelFactory.php: -------------------------------------------------------------------------------- 1 | id(); 13 | $table->string('type', 255); 14 | $table->string('code'); 15 | $table->longText('message'); 16 | $table->string('file', 255); 17 | $table->integer('line'); 18 | $table->text('trace'); 19 | $table->string('method'); 20 | $table->string('path', 255); 21 | $table->text('query'); 22 | $table->text('body'); 23 | $table->text('cookies'); 24 | $table->text('headers'); 25 | $table->string('ip'); 26 | $table->timestamps(); 27 | }); 28 | } 29 | 30 | public function down() 31 | { 32 | Schema::drop('filament_exceptions_table'); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "scripts": { 5 | "dev": "npx tailwindcss -i resources/css/filament-exceptions.css -o resources/dist/filament-exceptions.css --postcss --watch", 6 | "build": "npx tailwindcss -i resources/css/filament-exceptions.css -o resources/dist/filament-exceptions.css --postcss --minify && npm run purge", 7 | "purge": "filament-purge -i resources/dist/filament-exceptions.css -o resources/dist/filament-exceptions.css -v 3.x", 8 | "prettier": "npx prettier --write .", 9 | "deploy": "npm run build && npm run prettier" 10 | }, 11 | "devDependencies": { 12 | "@awcodes/filament-plugin-purge": "^1.1.1", 13 | "@tailwindcss/forms": "^0.5.4", 14 | "@tailwindcss/typography": "^0.5.9", 15 | "autoprefixer": "^10.4.14", 16 | "esbuild": "^0.18.13", 17 | "postcss": "^8.4.26", 18 | "postcss-import": "^15.1.0", 19 | "prettier": "^3.0.0", 20 | "prettier-plugin-blade": "^1.6.10", 21 | "prettier-plugin-tailwindcss": "^0.4.1", 22 | "tailwindcss": "^3.3.3" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /phpunit.xml.dist.bak: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 | 23 | tests 24 | 25 | 26 | 27 | 28 | ./src 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /pint.json: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "laravel", 3 | "rules": { 4 | "blank_line_before_statement": true, 5 | "concat_space": { 6 | "spacing": "one" 7 | }, 8 | "method_argument_space": true, 9 | "single_trait_insert_per_statement": true, 10 | "types_spaces": { 11 | "space": "single" 12 | } 13 | }, 14 | "exclude": ["build"] 15 | } 16 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | "postcss-import": {}, 4 | "tailwindcss/nesting": {}, 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /resources/css/filament-exceptions.css: -------------------------------------------------------------------------------- 1 | @tailwind components; 2 | @tailwind utilities; 3 | 4 | /* http://prismjs.com/download.html?themes=prism&languages=clike+php&plugins=line-highlight+line-numbers */ 5 | /** 6 | * prism.js default theme for JavaScript, CSS and HTML 7 | * Based on dabblet (http://dabblet.com) 8 | * @author Lea Verou 9 | */ 10 | 11 | code[class*="language-"], 12 | pre[class*="language-"] { 13 | color: inherit; 14 | background: inherit; 15 | text-shadow: none; 16 | font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; 17 | text-align: left; 18 | white-space: pre; 19 | word-spacing: normal; 20 | word-break: normal; 21 | word-wrap: normal; 22 | line-height: 1.5; 23 | 24 | -moz-tab-size: 1; 25 | -o-tab-size: 1; 26 | tab-size: 1; 27 | 28 | -webkit-hyphens: none; 29 | -moz-hyphens: none; 30 | -ms-hyphens: none; 31 | hyphens: none; 32 | } 33 | 34 | pre[class*="language-"]::selection, 35 | pre[class*="language-"] ::selection, 36 | code[class*="language-"]::selection, 37 | code[class*="language-"] ::selection { 38 | text-shadow: none; 39 | background: #b3d4fc; 40 | } 41 | 42 | @media print { 43 | code[class*="language-"], 44 | pre[class*="language-"] { 45 | text-shadow: none; 46 | } 47 | } 48 | 49 | /* Code blocks */ 50 | pre[class*="language-"] { 51 | padding: 1rem; 52 | margin: 0.5rem 0; 53 | overflow: auto; 54 | } 55 | 56 | :not(pre) > code[class*="language-"], 57 | pre[class*="language-"] { 58 | background: inherit; 59 | border-radius: 8px; 60 | } 61 | 62 | /* Inline code */ 63 | :not(pre) > code[class*="language-"] { 64 | padding: 0.4rem; 65 | border-radius: 0.4rem; 66 | white-space: normal; 67 | } 68 | 69 | .token.comment, 70 | .token.prolog, 71 | .token.doctype, 72 | .token.cdata { 73 | color: slategray; 74 | } 75 | 76 | .token.punctuation { 77 | color: #999; 78 | } 79 | 80 | .namespace { 81 | opacity: 0.7; 82 | } 83 | 84 | .token.property, 85 | .token.tag, 86 | .token.boolean, 87 | .token.number, 88 | .token.constant, 89 | .token.symbol, 90 | .token.deleted { 91 | color: #905; 92 | } 93 | 94 | .token.selector, 95 | .token.attr-name, 96 | .token.string, 97 | .token.char, 98 | .token.builtin, 99 | .token.inserted { 100 | color: #690; 101 | } 102 | 103 | .token.operator, 104 | .token.entity, 105 | .token.url, 106 | .language-css .token.string, 107 | .style .token.string { 108 | color: inherit; 109 | } 110 | 111 | .token.atrule, 112 | .token.attr-value, 113 | .token.keyword { 114 | color: #07a; 115 | } 116 | 117 | .token.function { 118 | color: #dd4a68; 119 | } 120 | 121 | .token.regex, 122 | .token.important, 123 | .token.variable { 124 | color: #e90; 125 | } 126 | 127 | .token.important, 128 | .token.bold { 129 | font-weight: bold; 130 | } 131 | .token.italic { 132 | font-style: italic; 133 | } 134 | 135 | .token.entity { 136 | cursor: help; 137 | } 138 | 139 | /* pre[data-line] { 140 | position: relative; 141 | padding: 1rem 0 1rem 3rem; 142 | } */ 143 | 144 | .line-highlight { 145 | position: absolute; 146 | left: 0; 147 | right: 0; 148 | padding: inherit 0; 149 | margin-top: 1rem; /* Same as .prism’s padding-top */ 150 | 151 | background: #77778675; 152 | 153 | pointer-events: none; 154 | 155 | line-height: inherit; 156 | white-space: pre-wrap; 157 | } 158 | 159 | .line-highlight:before, 160 | .line-highlight[data-end]:after { 161 | content: attr(data-start); 162 | position: absolute; 163 | top: 0.4rem; 164 | left: 0.6rem; 165 | min-width: 1rem; 166 | padding: 0 0.5rem; 167 | background-color: hsla(24, 20%, 50%, 0.4); 168 | color: hsl(24, 20%, 95%); 169 | font: bold 65%/1.5 sans-serif; 170 | text-align: center; 171 | vertical-align: 0.3rem; 172 | border-radius: 999px; 173 | text-shadow: none; 174 | box-shadow: 0 1px white; 175 | } 176 | 177 | .line-highlight[data-end]:after { 178 | content: attr(data-end); 179 | top: auto; 180 | bottom: 0.4rem; 181 | } 182 | 183 | pre.line-numbers { 184 | position: relative; 185 | padding-left: 3.8rem; 186 | counter-reset: linenumber; 187 | } 188 | 189 | pre.line-numbers > code { 190 | position: relative; 191 | } 192 | 193 | .line-numbers .line-numbers-rows { 194 | position: absolute; 195 | pointer-events: none; 196 | top: 0; 197 | font-size: 100%; 198 | left: -20rem; 199 | width: 3rem; 200 | /* works for line-numbers below 1000 lines */ 201 | letter-spacing: -1px; 202 | border-right: 1px solid #999; 203 | -webkit-user-select: none; 204 | -moz-user-select: none; 205 | -ms-user-select: none; 206 | user-select: none; 207 | } 208 | 209 | .line-numbers-rows > span { 210 | pointer-events: none; 211 | display: block; 212 | counter-increment: linenumber; 213 | } 214 | 215 | .line-numbers-rows > span:before { 216 | content: counter(linenumber); 217 | color: #999; 218 | display: block; 219 | padding-right: 0.8rem; 220 | text-align: right; 221 | } 222 | -------------------------------------------------------------------------------- /resources/dist/filament-exceptions.css: -------------------------------------------------------------------------------- 1 | .static { 2 | position: static; 3 | } 4 | 5 | .mb-4 { 6 | margin-bottom: 1rem; 7 | } 8 | 9 | .ml-2 { 10 | margin-left: 0.5rem; 11 | } 12 | 13 | .ml-4 { 14 | margin-left: 1rem; 15 | } 16 | 17 | .ml-auto { 18 | margin-left: auto; 19 | } 20 | 21 | .mr-2 { 22 | margin-right: 0.5rem; 23 | } 24 | 25 | .mt-1 { 26 | margin-top: 0.25rem; 27 | } 28 | 29 | .mt-2 { 30 | margin-top: 0.5rem; 31 | } 32 | 33 | .mt-4 { 34 | margin-top: 1rem; 35 | } 36 | 37 | .flex { 38 | display: flex; 39 | } 40 | 41 | .inline-flex { 42 | display: inline-flex; 43 | } 44 | 45 | .table { 46 | display: table; 47 | } 48 | 49 | .contents { 50 | display: contents; 51 | } 52 | 53 | .h-4 { 54 | height: 1rem; 55 | } 56 | 57 | .h-6 { 58 | height: 1.5rem; 59 | } 60 | 61 | .w-4 { 62 | width: 1rem; 63 | } 64 | 65 | .w-6 { 66 | width: 1.5rem; 67 | } 68 | 69 | .w-full { 70 | width: 100%; 71 | } 72 | 73 | .max-w-2xl { 74 | max-width: 42rem; 75 | } 76 | 77 | .basis-1\/12 { 78 | flex-basis: 8.333333%; 79 | } 80 | 81 | .basis-11\/12 { 82 | flex-basis: 91.666667%; 83 | } 84 | 85 | .flex-row { 86 | flex-direction: row; 87 | } 88 | 89 | .flex-col { 90 | flex-direction: column; 91 | } 92 | 93 | .flex-wrap { 94 | flex-wrap: wrap; 95 | } 96 | 97 | .items-center { 98 | align-items: center; 99 | } 100 | 101 | .justify-start { 102 | justify-content: flex-start; 103 | } 104 | 105 | .justify-center { 106 | justify-content: center; 107 | } 108 | 109 | .gap-x-1 { 110 | -moz-column-gap: 0.25rem; 111 | column-gap: 0.25rem; 112 | } 113 | 114 | .space-x-2 > :not([hidden]) ~ :not([hidden]) { 115 | --tw-space-x-reverse: 0; 116 | margin-right: calc(0.5rem * var(--tw-space-x-reverse)); 117 | margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); 118 | } 119 | 120 | .space-y-2 > :not([hidden]) ~ :not([hidden]) { 121 | --tw-space-y-reverse: 0; 122 | margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); 123 | margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); 124 | } 125 | 126 | .overflow-hidden { 127 | overflow: hidden; 128 | } 129 | 130 | .whitespace-normal { 131 | white-space: normal; 132 | } 133 | 134 | .break-words { 135 | overflow-wrap: break-word; 136 | } 137 | 138 | .break-all { 139 | word-break: break-all; 140 | } 141 | 142 | .rounded-lg { 143 | border-radius: 0.5rem; 144 | } 145 | 146 | .rounded-md { 147 | border-radius: 0.375rem; 148 | } 149 | 150 | .rounded-sm { 151 | border-radius: 0.125rem; 152 | } 153 | 154 | .rounded-xl { 155 | border-radius: 0.75rem; 156 | } 157 | 158 | .border-b { 159 | border-bottom-width: 1px; 160 | } 161 | 162 | .border-gray-200 { 163 | --tw-border-opacity: 1; 164 | border-color: rgba(var(--gray-200), var(--tw-border-opacity)); 165 | } 166 | 167 | .bg-danger-500\/10 { 168 | background-color: rgba(var(--danger-500), 0.1); 169 | } 170 | 171 | .bg-gray-100 { 172 | --tw-bg-opacity: 1; 173 | background-color: rgba(var(--gray-100), var(--tw-bg-opacity)); 174 | } 175 | 176 | .bg-gray-400 { 177 | --tw-bg-opacity: 1; 178 | background-color: rgba(var(--gray-400), var(--tw-bg-opacity)); 179 | } 180 | 181 | .bg-gray-50 { 182 | --tw-bg-opacity: 1; 183 | background-color: rgba(var(--gray-50), var(--tw-bg-opacity)); 184 | } 185 | 186 | .bg-gray-500\/10 { 187 | background-color: rgba(var(--gray-500), 0.1); 188 | } 189 | 190 | .bg-gray-950\/5 { 191 | background-color: rgba(var(--gray-950), 0.05); 192 | } 193 | 194 | .bg-primary-500\/10 { 195 | background-color: rgba(var(--primary-500), 0.1); 196 | } 197 | 198 | .bg-success-500\/10 { 199 | background-color: rgba(var(--success-500), 0.1); 200 | } 201 | 202 | .bg-warning-500\/10 { 203 | background-color: rgba(var(--warning-500), 0.1); 204 | } 205 | 206 | .bg-white { 207 | --tw-bg-opacity: 1; 208 | background-color: rgb(255 255 255 / var(--tw-bg-opacity)); 209 | } 210 | 211 | .p-1 { 212 | padding: 0.25rem; 213 | } 214 | 215 | .p-4 { 216 | padding: 1rem; 217 | } 218 | 219 | .px-2 { 220 | padding-left: 0.5rem; 221 | padding-right: 0.5rem; 222 | } 223 | 224 | .px-4 { 225 | padding-left: 1rem; 226 | padding-right: 1rem; 227 | } 228 | 229 | .px-6 { 230 | padding-left: 1.5rem; 231 | padding-right: 1.5rem; 232 | } 233 | 234 | .py-0 { 235 | padding-top: 0px; 236 | padding-bottom: 0px; 237 | } 238 | 239 | .py-0\.5 { 240 | padding-top: 0.125rem; 241 | padding-bottom: 0.125rem; 242 | } 243 | 244 | .py-1 { 245 | padding-top: 0.25rem; 246 | padding-bottom: 0.25rem; 247 | } 248 | 249 | .py-2 { 250 | padding-top: 0.5rem; 251 | padding-bottom: 0.5rem; 252 | } 253 | 254 | .py-3 { 255 | padding-top: 0.75rem; 256 | padding-bottom: 0.75rem; 257 | } 258 | 259 | .py-5 { 260 | padding-top: 1.25rem; 261 | padding-bottom: 1.25rem; 262 | } 263 | 264 | .pb-2 { 265 | padding-bottom: 0.5rem; 266 | } 267 | 268 | .pb-4 { 269 | padding-bottom: 1rem; 270 | } 271 | 272 | .pr-2 { 273 | padding-right: 0.5rem; 274 | } 275 | 276 | .text-left { 277 | text-align: left; 278 | } 279 | 280 | .font-mono { 281 | font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 282 | } 283 | 284 | .text-lg { 285 | font-size: 1.125rem; 286 | line-height: 1.75rem; 287 | } 288 | 289 | .text-sm { 290 | font-size: 0.875rem; 291 | line-height: 1.25rem; 292 | } 293 | 294 | .text-xs { 295 | font-size: 0.75rem; 296 | line-height: 1rem; 297 | } 298 | 299 | .font-medium { 300 | font-weight: 500; 301 | } 302 | 303 | .font-semibold { 304 | font-weight: 600; 305 | } 306 | 307 | .leading-5 { 308 | line-height: 1.25rem; 309 | } 310 | 311 | .leading-6 { 312 | line-height: 1.5rem; 313 | } 314 | 315 | .tracking-tight { 316 | letter-spacing: -0.025em; 317 | } 318 | 319 | .\!text-primary-600 { 320 | --tw-text-opacity: 1 !important; 321 | color: rgba(var(--primary-600), var(--tw-text-opacity)) !important; 322 | } 323 | 324 | .text-danger-700 { 325 | --tw-text-opacity: 1; 326 | color: rgba(var(--danger-700), var(--tw-text-opacity)); 327 | } 328 | 329 | .text-gray-400 { 330 | --tw-text-opacity: 1; 331 | color: rgba(var(--gray-400), var(--tw-text-opacity)); 332 | } 333 | 334 | .text-gray-500 { 335 | --tw-text-opacity: 1; 336 | color: rgba(var(--gray-500), var(--tw-text-opacity)); 337 | } 338 | 339 | .text-gray-600 { 340 | --tw-text-opacity: 1; 341 | color: rgba(var(--gray-600), var(--tw-text-opacity)); 342 | } 343 | 344 | .text-gray-700 { 345 | --tw-text-opacity: 1; 346 | color: rgba(var(--gray-700), var(--tw-text-opacity)); 347 | } 348 | 349 | .text-gray-800 { 350 | --tw-text-opacity: 1; 351 | color: rgba(var(--gray-800), var(--tw-text-opacity)); 352 | } 353 | 354 | .text-gray-900 { 355 | --tw-text-opacity: 1; 356 | color: rgba(var(--gray-900), var(--tw-text-opacity)); 357 | } 358 | 359 | .text-primary-600 { 360 | --tw-text-opacity: 1; 361 | color: rgba(var(--primary-600), var(--tw-text-opacity)); 362 | } 363 | 364 | .text-primary-700 { 365 | --tw-text-opacity: 1; 366 | color: rgba(var(--primary-700), var(--tw-text-opacity)); 367 | } 368 | 369 | .text-success-700 { 370 | --tw-text-opacity: 1; 371 | color: rgba(var(--success-700), var(--tw-text-opacity)); 372 | } 373 | 374 | .text-warning-700 { 375 | --tw-text-opacity: 1; 376 | color: rgba(var(--warning-700), var(--tw-text-opacity)); 377 | } 378 | 379 | .text-white { 380 | --tw-text-opacity: 1; 381 | color: rgb(255 255 255 / var(--tw-text-opacity)); 382 | } 383 | 384 | .shadow-none { 385 | --tw-shadow: 0 0 #0000; 386 | --tw-shadow-colored: 0 0 #0000; 387 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); 388 | } 389 | 390 | .outline-none { 391 | outline: 2px solid transparent; 392 | outline-offset: 2px; 393 | } 394 | 395 | .transition { 396 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; 397 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; 398 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; 399 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 400 | transition-duration: 150ms; 401 | } 402 | 403 | .duration-75 { 404 | transition-duration: 75ms; 405 | } 406 | 407 | /* http://prismjs.com/download.html?themes=prism&languages=clike+php&plugins=line-highlight+line-numbers */ 408 | 409 | /** 410 | * prism.js default theme for JavaScript, CSS and HTML 411 | * Based on dabblet (http://dabblet.com) 412 | * @author Lea Verou 413 | */ 414 | 415 | code[class*="language-"], 416 | pre[class*="language-"] { 417 | color: inherit; 418 | background: inherit; 419 | text-shadow: none; 420 | font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; 421 | text-align: left; 422 | white-space: pre; 423 | word-spacing: normal; 424 | word-break: normal; 425 | word-wrap: normal; 426 | line-height: 1.5; 427 | -moz-tab-size: 1; 428 | -o-tab-size: 1; 429 | tab-size: 1; 430 | -webkit-hyphens: none; 431 | hyphens: none; 432 | } 433 | 434 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 435 | text-shadow: none; 436 | background: #b3d4fc; 437 | } 438 | 439 | pre[class*="language-"]::selection, 440 | pre[class*="language-"] ::selection, 441 | code[class*="language-"]::selection, 442 | code[class*="language-"] ::selection { 443 | text-shadow: none; 444 | background: #b3d4fc; 445 | } 446 | 447 | @media print { 448 | code[class*="language-"], 449 | pre[class*="language-"] { 450 | text-shadow: none; 451 | } 452 | } 453 | 454 | /* Code blocks */ 455 | 456 | pre[class*="language-"] { 457 | padding: 1rem; 458 | margin: 0.5rem 0; 459 | overflow: auto; 460 | } 461 | 462 | :not(pre) > code[class*="language-"], 463 | pre[class*="language-"] { 464 | background: inherit; 465 | border-radius: 8px; 466 | } 467 | 468 | /* Inline code */ 469 | 470 | :not(pre) > code[class*="language-"] { 471 | padding: 0.4rem; 472 | border-radius: 0.4rem; 473 | white-space: normal; 474 | } 475 | 476 | .token.comment, 477 | .token.prolog, 478 | .token.doctype, 479 | .token.cdata { 480 | color: slategray; 481 | } 482 | 483 | .token.punctuation { 484 | color: #999; 485 | } 486 | 487 | .namespace { 488 | opacity: 0.7; 489 | } 490 | 491 | .token.property, 492 | .token.tag, 493 | .token.boolean, 494 | .token.number, 495 | .token.constant, 496 | .token.symbol, 497 | .token.deleted { 498 | color: #905; 499 | } 500 | 501 | .token.selector, 502 | .token.attr-name, 503 | .token.string, 504 | .token.char, 505 | .token.builtin, 506 | .token.inserted { 507 | color: #690; 508 | } 509 | 510 | .token.operator, 511 | .token.entity, 512 | .token.url, 513 | .language-css .token.string, 514 | .style .token.string { 515 | color: inherit; 516 | } 517 | 518 | .token.atrule, 519 | .token.attr-value, 520 | .token.keyword { 521 | color: #07a; 522 | } 523 | 524 | .token.function { 525 | color: #dd4a68; 526 | } 527 | 528 | .token.regex, 529 | .token.important, 530 | .token.variable { 531 | color: #e90; 532 | } 533 | 534 | .token.important, 535 | .token.bold { 536 | font-weight: bold; 537 | } 538 | 539 | .token.italic { 540 | font-style: italic; 541 | } 542 | 543 | .token.entity { 544 | cursor: help; 545 | } 546 | 547 | /* pre[data-line] { 548 | position: relative; 549 | padding: 1rem 0 1rem 3rem; 550 | } */ 551 | 552 | .line-highlight { 553 | position: absolute; 554 | left: 0; 555 | right: 0; 556 | padding: inherit 0; 557 | margin-top: 1rem; 558 | /* Same as .prism’s padding-top */ 559 | background: #77778675; 560 | pointer-events: none; 561 | line-height: inherit; 562 | white-space: pre-wrap; 563 | } 564 | 565 | .line-highlight:before, 566 | .line-highlight[data-end]:after { 567 | content: attr(data-start); 568 | position: absolute; 569 | top: 0.4rem; 570 | left: 0.6rem; 571 | min-width: 1rem; 572 | padding: 0 0.5rem; 573 | background-color: hsla(24, 20%, 50%, 0.4); 574 | color: hsl(24, 20%, 95%); 575 | font: bold 65%/1.5 sans-serif; 576 | text-align: center; 577 | vertical-align: 0.3rem; 578 | border-radius: 999px; 579 | text-shadow: none; 580 | box-shadow: 0 1px white; 581 | } 582 | 583 | .line-highlight[data-end]:after { 584 | content: attr(data-end); 585 | top: auto; 586 | bottom: 0.4rem; 587 | } 588 | 589 | pre.line-numbers { 590 | position: relative; 591 | padding-left: 3.8rem; 592 | counter-reset: linenumber; 593 | } 594 | 595 | pre.line-numbers > code { 596 | position: relative; 597 | } 598 | 599 | .line-numbers .line-numbers-rows { 600 | position: absolute; 601 | pointer-events: none; 602 | top: 0; 603 | font-size: 100%; 604 | left: -20rem; 605 | width: 3rem; 606 | /* works for line-numbers below 1000 lines */ 607 | letter-spacing: -1px; 608 | border-right: 1px solid #999; 609 | -webkit-user-select: none; 610 | -moz-user-select: none; 611 | user-select: none; 612 | } 613 | 614 | .line-numbers-rows > span { 615 | pointer-events: none; 616 | display: block; 617 | counter-increment: linenumber; 618 | } 619 | 620 | .line-numbers-rows > span:before { 621 | content: counter(linenumber); 622 | color: #999; 623 | display: block; 624 | padding-right: 0.8rem; 625 | text-align: right; 626 | } 627 | 628 | .even\:bg-white:nth-child(even) { 629 | --tw-bg-opacity: 1; 630 | background-color: rgb(255 255 255 / var(--tw-bg-opacity)); 631 | } 632 | 633 | .hover\:bg-gray-950\/5:hover { 634 | background-color: rgba(var(--gray-950), 0.05); 635 | } 636 | 637 | .focus\:bg-gray-950\/5:focus { 638 | background-color: rgba(var(--gray-950), 0.05); 639 | } 640 | 641 | :is([dir="rtl"] .rtl\:ml-0) { 642 | margin-left: 0px; 643 | } 644 | 645 | :is([dir="rtl"] .rtl\:mr-auto) { 646 | margin-right: auto; 647 | } 648 | 649 | :is([dir="rtl"] .rtl\:rotate-0) { 650 | --tw-rotate: 0deg; 651 | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); 652 | } 653 | 654 | :is([dir="rtl"] .rtl\:rotate-180) { 655 | --tw-rotate: 180deg; 656 | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); 657 | } 658 | 659 | :is([dir="rtl"] .rtl\:pl-2) { 660 | padding-left: 0.5rem; 661 | } 662 | 663 | :is(.dark .dark\:rounded-lg) { 664 | border-radius: 0.5rem; 665 | } 666 | 667 | :is(.dark .dark\:border) { 668 | border-width: 1px; 669 | } 670 | 671 | :is(.dark .dark\:border-r) { 672 | border-right-width: 1px; 673 | } 674 | 675 | :is(.dark .dark\:border-gray-600) { 676 | --tw-border-opacity: 1; 677 | border-color: rgba(var(--gray-600), var(--tw-border-opacity)); 678 | } 679 | 680 | :is(.dark .dark\:border-gray-700) { 681 | --tw-border-opacity: 1; 682 | border-color: rgba(var(--gray-700), var(--tw-border-opacity)); 683 | } 684 | 685 | :is(.dark .dark\:border-gray-800) { 686 | --tw-border-opacity: 1; 687 | border-color: rgba(var(--gray-800), var(--tw-border-opacity)); 688 | } 689 | 690 | :is(.dark .dark\:border-gray-950) { 691 | --tw-border-opacity: 1; 692 | border-color: rgba(var(--gray-950), var(--tw-border-opacity)); 693 | } 694 | 695 | :is(.dark .dark\:border-r-gray-200) { 696 | --tw-border-opacity: 1; 697 | border-right-color: rgba(var(--gray-200), var(--tw-border-opacity)); 698 | } 699 | 700 | :is(.dark .dark\:bg-gray-500\/20) { 701 | background-color: rgba(var(--gray-500), 0.2); 702 | } 703 | 704 | :is(.dark .dark\:bg-gray-800) { 705 | --tw-bg-opacity: 1; 706 | background-color: rgba(var(--gray-800), var(--tw-bg-opacity)); 707 | } 708 | 709 | :is(.dark .dark\:bg-gray-900) { 710 | --tw-bg-opacity: 1; 711 | background-color: rgba(var(--gray-900), var(--tw-bg-opacity)); 712 | } 713 | 714 | :is(.dark .dark\:bg-white\/5) { 715 | background-color: rgb(255 255 255 / 0.05); 716 | } 717 | 718 | :is(.dark .dark\:bg-white\/\[0\.03\]) { 719 | background-color: rgb(255 255 255 / 0.03); 720 | } 721 | 722 | :is(.dark .dark\:text-danger-500) { 723 | --tw-text-opacity: 1; 724 | color: rgba(var(--danger-500), var(--tw-text-opacity)); 725 | } 726 | 727 | :is(.dark .dark\:text-gray-100) { 728 | --tw-text-opacity: 1; 729 | color: rgba(var(--gray-100), var(--tw-text-opacity)); 730 | } 731 | 732 | :is(.dark .dark\:text-gray-200) { 733 | --tw-text-opacity: 1; 734 | color: rgba(var(--gray-200), var(--tw-text-opacity)); 735 | } 736 | 737 | :is(.dark .dark\:text-gray-300) { 738 | --tw-text-opacity: 1; 739 | color: rgba(var(--gray-300), var(--tw-text-opacity)); 740 | } 741 | 742 | :is(.dark .dark\:text-gray-400) { 743 | --tw-text-opacity: 1; 744 | color: rgba(var(--gray-400), var(--tw-text-opacity)); 745 | } 746 | 747 | :is(.dark .dark\:text-gray-50) { 748 | --tw-text-opacity: 1; 749 | color: rgba(var(--gray-50), var(--tw-text-opacity)); 750 | } 751 | 752 | :is(.dark .dark\:text-gray-500) { 753 | --tw-text-opacity: 1; 754 | color: rgba(var(--gray-500), var(--tw-text-opacity)); 755 | } 756 | 757 | :is(.dark .dark\:text-primary-400) { 758 | --tw-text-opacity: 1; 759 | color: rgba(var(--primary-400), var(--tw-text-opacity)); 760 | } 761 | 762 | :is(.dark .dark\:text-primary-500) { 763 | --tw-text-opacity: 1; 764 | color: rgba(var(--primary-500), var(--tw-text-opacity)); 765 | } 766 | 767 | :is(.dark .dark\:text-primary-600) { 768 | --tw-text-opacity: 1; 769 | color: rgba(var(--primary-600), var(--tw-text-opacity)); 770 | } 771 | 772 | :is(.dark .dark\:text-success-500) { 773 | --tw-text-opacity: 1; 774 | color: rgba(var(--success-500), var(--tw-text-opacity)); 775 | } 776 | 777 | :is(.dark .dark\:text-warning-500) { 778 | --tw-text-opacity: 1; 779 | color: rgba(var(--warning-500), var(--tw-text-opacity)); 780 | } 781 | 782 | :is(.dark .dark\:even\:bg-gray-600:nth-child(even)) { 783 | --tw-bg-opacity: 1; 784 | background-color: rgba(var(--gray-600), var(--tw-bg-opacity)); 785 | } 786 | 787 | :is(.dark .dark\:even\:bg-gray-700:nth-child(even)) { 788 | --tw-bg-opacity: 1; 789 | background-color: rgba(var(--gray-700), var(--tw-bg-opacity)); 790 | } 791 | 792 | :is(.dark .dark\:even\:text-gray-50:nth-child(even)) { 793 | --tw-text-opacity: 1; 794 | color: rgba(var(--gray-50), var(--tw-text-opacity)); 795 | } 796 | 797 | :is(.dark .dark\:hover\:bg-white\/5:hover) { 798 | background-color: rgb(255 255 255 / 0.05); 799 | } 800 | 801 | :is(.dark .dark\:focus\:bg-white\/5:focus) { 802 | background-color: rgb(255 255 255 / 0.05); 803 | } 804 | 805 | @media (min-width: 640px) { 806 | .sm\:col-span-2 { 807 | grid-column: span 2 / span 2; 808 | } 809 | 810 | .sm\:mt-0 { 811 | margin-top: 0px; 812 | } 813 | 814 | .sm\:grid { 815 | display: grid; 816 | } 817 | 818 | .sm\:grid-cols-3 { 819 | grid-template-columns: repeat(3, minmax(0, 1fr)); 820 | } 821 | 822 | .sm\:gap-4 { 823 | gap: 1rem; 824 | } 825 | 826 | .sm\:px-6 { 827 | padding-left: 1.5rem; 828 | padding-right: 1.5rem; 829 | } 830 | } 831 | 832 | @media (min-width: 768px) { 833 | .md\:flex-row { 834 | flex-direction: row; 835 | } 836 | } 837 | -------------------------------------------------------------------------------- /resources/dist/filament-exceptions.js: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=clike+php&plugins=line-highlight+line-numbers */ 2 | var _self = 3 | "undefined" != typeof window 4 | ? window 5 | : "undefined" != typeof WorkerGlobalScope && 6 | self instanceof WorkerGlobalScope 7 | ? self 8 | : {}, 9 | Prism = (function () { 10 | var e = /\blang(?:uage)?-(\w+)\b/i, 11 | t = 0, 12 | n = (_self.Prism = { 13 | util: { 14 | encode: function (e) { 15 | return e instanceof a 16 | ? new a(e.type, n.util.encode(e.content), e.alias) 17 | : "Array" === n.util.type(e) 18 | ? e.map(n.util.encode) 19 | : e 20 | .replace(/&/g, "&") 21 | .replace(/ e.length) break e; 221 | if (!(v instanceof a)) { 222 | u.lastIndex = 0; 223 | var b = u.exec(v), 224 | k = 1; 225 | if (!b && h && m != r.length - 1) { 226 | if ( 227 | ((u.lastIndex = y), 228 | (b = u.exec(e)), 229 | !b) 230 | ) 231 | break; 232 | for ( 233 | var w = 234 | b.index + 235 | (c ? b[1].length : 0), 236 | _ = b.index + b[0].length, 237 | A = m, 238 | P = y, 239 | x = r.length; 240 | x > A && _ > P; 241 | ++A 242 | ) 243 | (P += r[A].length), 244 | w >= P && (++m, (y = P)); 245 | if ( 246 | r[m] instanceof a || 247 | r[A - 1].greedy 248 | ) 249 | continue; 250 | (k = A - m), 251 | (v = e.slice(y, P)), 252 | (b.index -= y); 253 | } 254 | if (b) { 255 | c && (f = b[1].length); 256 | var w = b.index + f, 257 | b = b[0].slice(f), 258 | _ = w + b.length, 259 | O = v.slice(0, w), 260 | S = v.slice(_), 261 | j = [m, k]; 262 | O && j.push(O); 263 | var N = new a( 264 | l, 265 | g ? n.tokenize(b, g) : b, 266 | d, 267 | b, 268 | h 269 | ); 270 | j.push(N), 271 | S && j.push(S), 272 | Array.prototype.splice.apply( 273 | r, 274 | j 275 | ); 276 | } 277 | } 278 | } 279 | } 280 | } 281 | return r; 282 | }, 283 | hooks: { 284 | all: {}, 285 | add: function (e, t) { 286 | var a = n.hooks.all; 287 | (a[e] = a[e] || []), a[e].push(t); 288 | }, 289 | run: function (e, t) { 290 | var a = n.hooks.all[e]; 291 | if (a && a.length) 292 | for (var r, i = 0; (r = a[i++]); ) r(t); 293 | }, 294 | }, 295 | }), 296 | a = (n.Token = function (e, t, n, a, r) { 297 | (this.type = e), 298 | (this.content = t), 299 | (this.alias = n), 300 | (this.length = 0 | (a || "").length), 301 | (this.greedy = !!r); 302 | }); 303 | if ( 304 | ((a.stringify = function (e, t, r) { 305 | if ("string" == typeof e) return e; 306 | if ("Array" === n.util.type(e)) 307 | return e 308 | .map(function (n) { 309 | return a.stringify(n, t, e); 310 | }) 311 | .join(""); 312 | var i = { 313 | type: e.type, 314 | content: a.stringify(e.content, t, r), 315 | tag: "span", 316 | classes: ["token", e.type], 317 | attributes: {}, 318 | language: t, 319 | parent: r, 320 | }; 321 | if ( 322 | ("comment" == i.type && (i.attributes.spellcheck = "true"), 323 | e.alias) 324 | ) { 325 | var l = 326 | "Array" === n.util.type(e.alias) ? e.alias : [e.alias]; 327 | Array.prototype.push.apply(i.classes, l); 328 | } 329 | n.hooks.run("wrap", i); 330 | var o = ""; 331 | for (var s in i.attributes) 332 | o += 333 | (o ? " " : "") + 334 | s + 335 | '="' + 336 | (i.attributes[s] || "") + 337 | '"'; 338 | return ( 339 | "<" + 340 | i.tag + 341 | ' class="' + 342 | i.classes.join(" ") + 343 | '"' + 344 | (o ? " " + o : "") + 345 | ">" + 346 | i.content + 347 | "" 350 | ); 351 | }), 352 | !_self.document) 353 | ) 354 | return _self.addEventListener 355 | ? (_self.addEventListener( 356 | "message", 357 | function (e) { 358 | var t = JSON.parse(e.data), 359 | a = t.language, 360 | r = t.code, 361 | i = t.immediateClose; 362 | _self.postMessage(n.highlight(r, n.languages[a], a)), 363 | i && _self.close(); 364 | }, 365 | !1 366 | ), 367 | _self.Prism) 368 | : _self.Prism; 369 | var r = 370 | document.currentScript || 371 | [].slice.call(document.getElementsByTagName("script")).pop(); 372 | return ( 373 | r && 374 | ((n.filename = r.src), 375 | document.addEventListener && 376 | !r.hasAttribute("data-manual") && 377 | ("loading" !== document.readyState 378 | ? window.requestAnimationFrame 379 | ? window.requestAnimationFrame(n.highlightAll) 380 | : window.setTimeout(n.highlightAll, 16) 381 | : document.addEventListener( 382 | "DOMContentLoaded", 383 | n.highlightAll 384 | ))), 385 | _self.Prism 386 | ); 387 | })(); 388 | "undefined" != typeof module && module.exports && (module.exports = Prism), 389 | "undefined" != typeof global && (global.Prism = Prism); 390 | Prism.languages.clike = { 391 | comment: [ 392 | { pattern: /(^|[^\\])\/\*[\w\W]*?\*\//, lookbehind: !0 }, 393 | { pattern: /(^|[^\\:])\/\/.*/, lookbehind: !0 }, 394 | ], 395 | string: { 396 | pattern: /(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, 397 | greedy: !0, 398 | }, 399 | "class-name": { 400 | pattern: 401 | /((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i, 402 | lookbehind: !0, 403 | inside: { punctuation: /(\.|\\)/ }, 404 | }, 405 | keyword: 406 | /\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/, 407 | boolean: /\b(true|false)\b/, 408 | function: /[a-z0-9_]+(?=\()/i, 409 | number: /\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i, 410 | operator: /--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/, 411 | punctuation: /[{}[\];(),.:]/, 412 | }; 413 | (Prism.languages.php = Prism.languages.extend("clike", { 414 | keyword: 415 | /\b(and|or|xor|array|as|break|case|cfunction|class|const|continue|declare|default|die|do|else|elseif|enddeclare|endfor|endforeach|endif|endswitch|endwhile|extends|for|foreach|function|include|include_once|global|if|new|return|static|switch|use|require|require_once|var|while|abstract|interface|public|implements|private|protected|parent|throw|null|echo|print|trait|namespace|final|yield|goto|instanceof|finally|try|catch)\b/i, 416 | constant: /\b[A-Z0-9_]{2,}\b/, 417 | comment: { 418 | pattern: /(^|[^\\])(?:\/\*[\w\W]*?\*\/|\/\/.*)/, 419 | lookbehind: !0, 420 | greedy: !0, 421 | }, 422 | })), 423 | Prism.languages.insertBefore("php", "class-name", { 424 | "shell-comment": { 425 | pattern: /(^|[^\\])#.*/, 426 | lookbehind: !0, 427 | alias: "comment", 428 | }, 429 | }), 430 | Prism.languages.insertBefore("php", "keyword", { 431 | delimiter: /\?>|<\?(?:php)?/i, 432 | variable: /\$\w+\b/i, 433 | package: { 434 | pattern: /(\\|namespace\s+|use\s+)[\w\\]+/, 435 | lookbehind: !0, 436 | inside: { punctuation: /\\/ }, 437 | }, 438 | }), 439 | Prism.languages.insertBefore("php", "operator", { 440 | property: { pattern: /(->)[\w]+/, lookbehind: !0 }, 441 | }), 442 | Prism.languages.markup && 443 | (Prism.hooks.add("before-highlight", function (e) { 444 | "php" === e.language && 445 | ((e.tokenStack = []), 446 | (e.backupCode = e.code), 447 | (e.code = e.code.replace( 448 | /(?:<\?php|<\?)[\w\W]*?(?:\?>)/gi, 449 | function (a) { 450 | return ( 451 | e.tokenStack.push(a), 452 | "{{{PHP" + e.tokenStack.length + "}}}" 453 | ); 454 | } 455 | ))); 456 | }), 457 | Prism.hooks.add("before-insert", function (e) { 458 | "php" === e.language && 459 | ((e.code = e.backupCode), delete e.backupCode); 460 | }), 461 | Prism.hooks.add("after-highlight", function (e) { 462 | if ("php" === e.language) { 463 | for (var a, n = 0; (a = e.tokenStack[n]); n++) 464 | e.highlightedCode = e.highlightedCode.replace( 465 | "{{{PHP" + (n + 1) + "}}}", 466 | Prism.highlight(a, e.grammar, "php").replace( 467 | /\$/g, 468 | "$$$$" 469 | ) 470 | ); 471 | e.element.innerHTML = e.highlightedCode; 472 | } 473 | }), 474 | Prism.hooks.add("wrap", function (e) { 475 | "php" === e.language && 476 | "markup" === e.type && 477 | (e.content = e.content.replace( 478 | /(\{\{\{PHP[0-9]+\}\}\})/g, 479 | '$1' 480 | )); 481 | }), 482 | Prism.languages.insertBefore("php", "comment", { 483 | markup: { 484 | pattern: /<[^?]\/?(.*?)>/, 485 | inside: Prism.languages.markup, 486 | }, 487 | php: /\{\{\{PHP[0-9]+\}\}\}/, 488 | })); 489 | Prism.languages.sql = { 490 | comment: { 491 | pattern: 492 | /(^|[^\\])(\/\*[\w\W]*?\*\/|((--)|(\/\/)).*?(\r?\n|$))/g, 493 | lookbehind: !0, 494 | }, 495 | string: /("|')(\\?.)*?\1/g, 496 | keyword: 497 | /\b(ACTION|ADD|AFTER|ALGORITHM|ALTER|ANALYZE|APPLY|AS|AS|ASC|AUTHORIZATION|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADE|CASCADED|CASE|CHAIN|CHAR VARYING|CHARACTER VARYING|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLUMN|COLUMNS|COMMENT|COMMIT|COMMITTED|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATA|DATABASE|DATABASES|DATETIME|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DOUBLE PRECISION|DROP|DUMMY|DUMP|DUMPFILE|DUPLICATE KEY|ELSE|ENABLE|ENCLOSED BY|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPE|ESCAPED BY|EXCEPT|EXEC|EXECUTE|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR|FOR EACH ROW|FORCE|FOREIGN|FREETEXT|FREETEXTTABLE|FROM|FULL|FUNCTION|GEOMETRY|GEOMETRYCOLLECTION|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|IDENTITY|IDENTITY_INSERT|IDENTITYCOL|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTO|INVOKER|ISOLATION LEVEL|JOIN|KEY|KEYS|KILL|LANGUAGE SQL|LAST|LEFT|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONGBLOB|LONGTEXT|MATCH|MATCHED|MEDIUMBLOB|MEDIUMINT|MEDIUMTEXT|MERGE|MIDDLEINT|MODIFIES SQL DATA|MODIFY|MULTILINESTRING|MULTIPOINT|MULTIPOLYGON|NATIONAL|NATIONAL CHAR VARYING|NATIONAL CHARACTER|NATIONAL CHARACTER VARYING|NATIONAL VARCHAR|NATURAL|NCHAR|NCHAR VARCHAR|NEXT|NO|NO SQL|NOCHECK|NOCYCLE|NONCLUSTERED|NULLIF|NUMERIC|OF|OFF|OFFSETS|ON|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPTIMIZE|OPTION|OPTIONALLY|ORDER|OUT|OUTER|OUTFILE|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREV|PRIMARY|PRINT|PRIVILEGES|PROC|PROCEDURE|PUBLIC|PURGE|QUICK|RAISERROR|READ|READS SQL DATA|READTEXT|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEATABLE|REPLICATION|REQUIRE|RESTORE|RESTRICT|RETURN|RETURNS|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROWCOUNT|ROWGUIDCOL|ROWS?|RTREE|RULE|SAVE|SAVEPOINT|SCHEMA|SELECT|SERIAL|SERIALIZABLE|SESSION|SESSION_USER|SET|SETUSER|SHARE MODE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|START|STARTING BY|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLE|TABLES|TABLESPACE|TEMPORARY|TEMPTABLE|TERMINATED BY|TEXT|TEXTSIZE|THEN|TIMESTAMP|TINYBLOB|TINYINT|TINYTEXT|TO|TOP|TRAN|TRANSACTION|TRANSACTIONS|TRIGGER|TRUNCATE|TSEQUAL|TYPE|TYPES|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNPIVOT|UPDATE|UPDATETEXT|USAGE|USE|USER|USING|VALUE|VALUES|VARBINARY|VARCHAR|VARCHARACTER|VARYING|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH|WITH ROLLUP|WITHIN|WORK|WRITE|WRITETEXT)\b/gi, 498 | boolean: /\b(TRUE|FALSE|NULL)\b/gi, 499 | number: /\b-?(0x)?\d*\.?[\da-f]+\b/g, 500 | operator: 501 | /\b(ALL|AND|ANY|BETWEEN|EXISTS|IN|LIKE|NOT|OR|IS|UNIQUE|CHARACTER SET|COLLATE|DIV|OFFSET|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b|[-+]{1}|!|=?<|=?>|={1}|(&){1,2}|\|?\||\?|\*|\//gi, 502 | ignore: /&(lt|gt|amp);/gi, 503 | punctuation: /[;[\]()`,.]/g, 504 | }; 505 | !(function () { 506 | function e(e, t) { 507 | return Array.prototype.slice.call((t || document).querySelectorAll(e)); 508 | } 509 | function t(e, t) { 510 | return ( 511 | (t = " " + t + " "), 512 | (" " + e.className + " ").replace(/[\n\t]/g, " ").indexOf(t) > -1 513 | ); 514 | } 515 | function n(e, n, i) { 516 | for ( 517 | var o, 518 | a = n.replace(/\s+/g, "").split(","), 519 | l = +e.getAttribute("data-line-offset") || 0, 520 | d = r() ? parseInt : parseFloat, 521 | c = d(getComputedStyle(e).lineHeight), 522 | s = 0; 523 | (o = a[s++]); 524 | 525 | ) { 526 | o = o.split("-"); 527 | var u = +o[0], 528 | m = +o[1] || u, 529 | h = document.createElement("div"); 530 | (h.textContent = Array(m - u + 2).join(" \n")), 531 | h.setAttribute("aria-hidden", "true"), 532 | (h.className = (i || "") + " line-highlight"), 533 | t(e, "line-numbers") || 534 | (h.setAttribute("data-start", u), 535 | m > u && h.setAttribute("data-end", m)), 536 | (h.style.top = (u - l - 1) * c + "px"), 537 | t(e, "line-numbers") 538 | ? e.appendChild(h) 539 | : (e.querySelector("code") || e).appendChild(h); 540 | } 541 | } 542 | function i() { 543 | var t = location.hash.slice(1); 544 | e(".temporary.line-highlight").forEach(function (e) { 545 | e.parentNode.removeChild(e); 546 | }); 547 | var i = (t.match(/\.([\d,-]+)$/) || [, ""])[1]; 548 | if (i && !document.getElementById(t)) { 549 | var r = t.slice(0, t.lastIndexOf(".")), 550 | o = document.getElementById(r); 551 | o && 552 | (o.hasAttribute("data-line") || o.setAttribute("data-line", ""), 553 | n(o, i, "temporary "), 554 | document 555 | .querySelector(".temporary.line-highlight") 556 | .scrollIntoView()); 557 | } 558 | } 559 | if ( 560 | "undefined" != typeof self && 561 | self.Prism && 562 | self.document && 563 | document.querySelector 564 | ) { 565 | var r = (function () { 566 | var e; 567 | return function () { 568 | if ("undefined" == typeof e) { 569 | var t = document.createElement("div"); 570 | (t.style.fontSize = "13px"), 571 | (t.style.lineHeight = "1.5"), 572 | (t.style.padding = 0), 573 | (t.style.border = 0), 574 | (t.innerHTML = " 
 "), 575 | document.body.appendChild(t), 576 | (e = 38 === t.offsetHeight), 577 | document.body.removeChild(t); 578 | } 579 | return e; 580 | }; 581 | })(), 582 | o = 0; 583 | Prism.hooks.add("complete", function (t) { 584 | var r = t.element.parentNode, 585 | a = r && r.getAttribute("data-line"); 586 | r && 587 | a && 588 | /pre/i.test(r.nodeName) && 589 | (clearTimeout(o), 590 | e(".line-highlight", r).forEach(function (e) { 591 | e.parentNode.removeChild(e); 592 | }), 593 | n(r, a), 594 | (o = setTimeout(i, 1))); 595 | }), 596 | window.addEventListener && window.addEventListener("hashchange", i); 597 | } 598 | })(); 599 | !(function () { 600 | "undefined" != typeof self && 601 | self.Prism && 602 | self.document && 603 | Prism.hooks.add("complete", function (e) { 604 | if (e.code) { 605 | var t = e.element.parentNode, 606 | s = /\s*\bline-numbers\b\s*/; 607 | if ( 608 | t && 609 | /pre/i.test(t.nodeName) && 610 | (s.test(t.className) || s.test(e.element.className)) && 611 | !e.element.querySelector(".line-numbers-rows") 612 | ) { 613 | s.test(e.element.className) && 614 | (e.element.className = e.element.className.replace( 615 | s, 616 | "" 617 | )), 618 | s.test(t.className) || (t.className += " line-numbers"); 619 | var n, 620 | a = e.code.match(/\n(?!$)/g), 621 | l = a ? a.length + 1 : 1, 622 | r = new Array(l + 1); 623 | (r = r.join("")), 624 | (n = document.createElement("span")), 625 | n.setAttribute("aria-hidden", "true"), 626 | (n.className = "line-numbers-rows"), 627 | (n.innerHTML = r), 628 | t.hasAttribute("data-start") && 629 | (t.style.counterReset = 630 | "linenumber " + 631 | (parseInt(t.getAttribute("data-start"), 10) - 632 | 1)), 633 | e.element.appendChild(n); 634 | } 635 | } 636 | }); 637 | })(); 638 | 639 | 640 | -------------------------------------------------------------------------------- /resources/lang/cs/filament-exceptions.php: -------------------------------------------------------------------------------- 1 | [ 6 | 'model' => 'Výjimka', 7 | 'model_plural' => 'Výjimky', 8 | 'navigation' => 'Výjimka', 9 | 'navigation_group' => 'Nastavení', 10 | 11 | 'tabs' => [ 12 | 'exception' => 'Výjimka', 13 | 'headers' => 'Hlavičky', 14 | 'cookies' => 'Cookies', 15 | 'body' => 'Tělo', 16 | 'queries' => 'Dotazy', 17 | ], 18 | ], 19 | 20 | 'empty_list' => 'Hurá! Posaďte se a užívejte si 😎', 21 | 22 | 'columns' => [ 23 | 'method' => 'Metoda', 24 | 'path' => 'Cesta', 25 | 'type' => 'Typ', 26 | 'code' => 'Kód', 27 | 'ip' => 'IP', 28 | 'occurred_at' => 'Nastalo v', 29 | ], 30 | 31 | ]; 32 | -------------------------------------------------------------------------------- /resources/lang/en/filament-exceptions.php: -------------------------------------------------------------------------------- 1 | [ 6 | 'model' => 'Exception', 7 | 'model_plural' => 'Exceptions', 8 | 'navigation' => 'Exception', 9 | 'navigation_group' => 'Settings', 10 | 11 | 'tabs' => [ 12 | 'exception' => 'Exception', 13 | 'headers' => 'Headers', 14 | 'cookies' => 'Cookies', 15 | 'body' => 'Body', 16 | 'queries' => 'Queries', 17 | ], 18 | ], 19 | 20 | 'empty_list' => 'Horray! just sit back & enjoy 😎', 21 | 22 | 'columns' => [ 23 | 'method' => 'Method', 24 | 'path' => 'Path', 25 | 'type' => 'Type', 26 | 'code' => 'Code', 27 | 'ip' => 'IP', 28 | 'occurred_at' => 'Occurred at', 29 | ], 30 | 31 | ]; 32 | -------------------------------------------------------------------------------- /resources/lang/sk/filament-exceptions.php: -------------------------------------------------------------------------------- 1 | [ 6 | 'model' => 'Výnimka', 7 | 'model_plural' => 'Výnimky', 8 | 'navigation' => 'Výnimka', 9 | 'navigation_group' => 'Nastavenia', 10 | 11 | 'tabs' => [ 12 | 'exception' => 'Výnimka', 13 | 'headers' => 'Hlavičky', 14 | 'cookies' => 'Cookies', 15 | 'body' => 'Telo', 16 | 'queries' => 'Dotazy', 17 | ], 18 | ], 19 | 20 | 'empty_list' => 'Hurá! len si sadnite a užívajte si 😎', 21 | 22 | 'columns' => [ 23 | 'method' => 'Metóda', 24 | 'path' => 'Cesta', 25 | 'type' => 'Typ', 26 | 'code' => 'Kód', 27 | 'ip' => 'IP', 28 | 'occurred_at' => 'Nastalo o', 29 | ], 30 | 31 | ]; 32 | -------------------------------------------------------------------------------- /resources/views/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezhanSalleh/filament-exceptions/63e1f053c6d50d847bb322ee121e65e50f31be1a/resources/views/.gitkeep -------------------------------------------------------------------------------- /resources/views/body.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/views/components/code-preview.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'contents' => '', 3 | ]) 4 | 5 |
6 |
7 |
8 | @forelse (collect(json_decode($contents,true))->sortKeys()->all() as $name => $values) 9 |
11 |
13 | {{ implode('-', array_map('ucfirst', explode('-', $name))) }} 14 |
15 |
17 | {{ is_array($values) ? implode(' ', collect($values)->flatten()->toArray()) : $values }} 18 |
19 |
20 | @empty 21 |
[]
22 | @endforelse 23 | 24 |
25 |
26 |
27 | -------------------------------------------------------------------------------- /resources/views/components/query-preview.blade.php: -------------------------------------------------------------------------------- 1 | @props(['queries']) 2 | 3 | @forelse (collect(json_decode($queries,true))->all() ?? [] as $query) 4 |
5 | 6 |
8 |
10 | 12 | 13 | {{ str($query['time'])->append('MS') }} 14 | 15 |
16 |
18 | 20 | 21 | {{ str($query['connection_name'])->upper() }} 22 | 23 |
24 |
25 |
26 | 27 | {{ $query['sql'] }} 28 | 29 |
30 | 31 | @if (count($query['bindings'])) 32 |
33 |
35 | @foreach ($query['bindings'] as $key => $value) 36 |
37 |
{{ intVal($key) + 1 }} 38 |
39 |
41 | {{ $value }}
42 |
43 | @endforeach 44 |
45 |
46 | @endif 47 |
48 | @empty 49 | [] 50 | @endforelse 51 | -------------------------------------------------------------------------------- /resources/views/cookies.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/views/exception.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @foreach ($this->frames as $index => $frame) 3 |
13 |
14 |
16 | 20 | 25 | 26 | {{ str($frame->file())->replace(base_path() . '/', '') }} 27 | in {{ $frame->method() }} 28 | at line 29 | {{ $frame->line() }} 31 | 32 | 33 |
34 |
35 | {{-- class="font-mono break-all sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6" --}} 36 |
37 |
38 |
39 |                           
40 |                                 {!! $frame->getCodeBlock()->output() !!}
41 |                           
42 |                     
43 | @if (count($frame->args())) 44 | 46 | 47 | @foreach ($frame->args() as $name => $val) 48 | 49 | 50 | 51 | 52 | 53 | @endforeach 54 | 55 |
 {{ $name }}{{ $val }}
56 | @endif 57 |
58 |
59 |
60 | @endforeach 61 |
62 | -------------------------------------------------------------------------------- /resources/views/headers.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/views/query.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/views/view-exception.blade.php: -------------------------------------------------------------------------------- 1 | @php 2 | $method = $record->method; 3 | $methodColor = match ($method) { 4 | 'DELETE' => \Illuminate\Support\Arr::toCssClasses(['text-danger-700 bg-danger-500/10 dark:text-danger-500']), 5 | 'POST' => \Illuminate\Support\Arr::toCssClasses(['text-primary-700 bg-primary-500/10 dark:text-primary-500']), 6 | 'GET' => \Illuminate\Support\Arr::toCssClasses(['text-success-700 bg-success-500/10 dark:text-success-500']), 7 | 'PUT' => \Illuminate\Support\Arr::toCssClasses(['text-warning-700 bg-warning-500/10 dark:text-warning-500']), 8 | 'PATCH', 'OPTIONS' => \Illuminate\Support\Arr::toCssClasses(['text-gray-700 bg-gray-500/10 dark:text-gray-300 dark:bg-gray-500/20']), 9 | default => \Illuminate\Support\Arr::toCssClasses(['text-gray-700 bg-gray-500/10 dark:text-gray-300 dark:bg-gray-500/20']), 10 | }; 11 | @endphp 12 | getResource()::getSlug()), 16 | 'fi-resource-record-' . $record->getKey(), 17 | ]) 18 | > 19 | 20 |
22 |

23 | {{ $method }} 24 | {{ $record->path }} 26 | 27 |

28 |
29 | 30 | {{ __('filament-exceptions::filament-exceptions.columns.occurred_at') }}: 31 | 32 | {{ $record->created_at->toDateTimeString() }} 33 | 34 |
35 |
36 | {{ $record->message }} 37 |
38 |
39 | @php 40 | $relationManagers = $this->getRelationManagers(); 41 | @endphp 42 | 43 | @if ((! $this->hasCombinedRelationManagerTabsWithContent()) || (! count($relationManagers))) 44 | @if ($this->hasInfolist()) 45 | {{ $this->infolist }} 46 | @else 47 |
50 | {{ $this->form }} 51 |
52 | @endif 53 | @endif 54 |
55 | 56 | -------------------------------------------------------------------------------- /src/Commands/InstallCommand.php: -------------------------------------------------------------------------------- 1 | Filament Exceptions` plugin for filament.'; 16 | 17 | public function handle(): int 18 | { 19 | $this->call('vendor:publish', [ 20 | '--tag' => 'filament-exceptions-migrations', 21 | '--force' => $this->option('force'), 22 | ]); 23 | 24 | $this->call('migrate', [ 25 | '--force' => $this->option('force'), 26 | ]); 27 | 28 | $this->components->info('Filament Exceptions🐞... installed!'); 29 | 30 | $this->askToStar(); 31 | 32 | return self::SUCCESS; 33 | } 34 | 35 | protected function askToStar(): void 36 | { 37 | if ($this->option('no-interaction')) { 38 | return; 39 | } 40 | 41 | if (confirm( 42 | label: 'All done! Would you like to show some love by starring the `Filament Exceptions` repo on GitHub?', 43 | default: true, 44 | )) { 45 | if (PHP_OS_FAMILY === 'Darwin') { 46 | exec('open https://github.com/bezhanSalleh/filament-exceptions'); 47 | } 48 | if (PHP_OS_FAMILY === 'Linux') { 49 | exec('xdg-open https://github.com/bezhanSalleh/filament-exceptions'); 50 | } 51 | if (PHP_OS_FAMILY === 'Windows') { 52 | exec('start https://github.com/bezhanSalleh/filament-exceptions'); 53 | } 54 | 55 | $this->components->info('Thank you!'); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Concerns/HasLabels.php: -------------------------------------------------------------------------------- 1 | modelLabel = $label; 22 | 23 | return $this; 24 | } 25 | 26 | public function pluralModelLabel(string | Closure | null $label): static 27 | { 28 | $this->pluralModelLabel = $label; 29 | 30 | return $this; 31 | } 32 | 33 | public function titleCaseModelLabel(bool | Closure $condition = true): static 34 | { 35 | $this->hasTitleCaseModelLabel = $condition; 36 | 37 | return $this; 38 | } 39 | 40 | public function globallySearchable(bool | Closure $condition = true): static 41 | { 42 | $this->isGloballySearchable = $condition; 43 | 44 | return $this; 45 | } 46 | 47 | public function getModelLabel(): string 48 | { 49 | return $this->evaluate($this->modelLabel ?? __('filament-exceptions::filament-exceptions.labels.model')); 50 | } 51 | 52 | public function getPluralModelLabel(): string 53 | { 54 | return $this->evaluate($this->pluralModelLabel ?? __('filament-exceptions::filament-exceptions.labels.model_plural')); 55 | } 56 | 57 | public function hasTitleCaseModelLabel(): bool 58 | { 59 | return $this->evaluate($this->hasTitleCaseModelLabel); 60 | } 61 | 62 | public function canGloballySearch(): bool 63 | { 64 | return $this->evaluate($this->isGloballySearchable); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Concerns/HasModelPruneInterval.php: -------------------------------------------------------------------------------- 1 | modelPruneInterval = $interval; 16 | 17 | return $this; 18 | } 19 | 20 | public function getModelPruneInterval(): Carbon 21 | { 22 | return $this->modelPruneInterval ?? now()->subWeek(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Concerns/HasNavigation.php: -------------------------------------------------------------------------------- 1 | | Closure | null */ 14 | protected string | Closure | null $cluster = null; 15 | 16 | protected bool | Closure $shouldEnableNavigationBadge = false; 17 | 18 | protected string | array | Closure | null $navigationBadgeColor = null; 19 | 20 | protected string | Closure | null $navigationGroup = null; 21 | 22 | protected string | Closure | null $navigationParentItem = null; 23 | 24 | protected string | Closure | null $navigationIcon = null; 25 | 26 | protected string | Closure | null $activeNavigationIcon = null; 27 | 28 | protected string | Closure | null $navigationLabel = null; 29 | 30 | protected int | Closure | null $navigationSort = null; 31 | 32 | protected string | Closure | null $slug = null; 33 | 34 | protected bool | Closure $shouldRegisterNavigation = true; 35 | 36 | protected SubNavigationPosition | Closure $subNavigationPosition = SubNavigationPosition::Start; 37 | 38 | // Setters 39 | public function cluster(string | Closure | null $cluster): static 40 | { 41 | $this->cluster = $cluster; 42 | 43 | return $this; 44 | } 45 | 46 | public function navigationBadge(bool | Closure $condition = true): static 47 | { 48 | $this->shouldEnableNavigationBadge = $condition; 49 | 50 | return $this; 51 | } 52 | 53 | public function navigationBadgeColor(string | array | Closure $color): static 54 | { 55 | $this->navigationBadgeColor = $color; 56 | 57 | return $this; 58 | } 59 | 60 | public function navigationGroup(string | Closure | null $group): static 61 | { 62 | $this->navigationGroup = $group; 63 | 64 | return $this; 65 | } 66 | 67 | public function navigationParentItem(string | Closure | null $item): static 68 | { 69 | $this->navigationParentItem = $item; 70 | 71 | return $this; 72 | } 73 | 74 | public function navigationIcon(string | Closure | null $icon): static 75 | { 76 | $this->navigationIcon = $icon; 77 | 78 | return $this; 79 | } 80 | 81 | public function activeNavigationIcon(string | Closure | null $icon): static 82 | { 83 | $this->activeNavigationIcon = $icon; 84 | 85 | return $this; 86 | } 87 | 88 | public function navigationLabel(string | Closure | null $label): static 89 | { 90 | $this->navigationLabel = $label; 91 | 92 | return $this; 93 | } 94 | 95 | public function navigationSort(int | Closure | null $sort): static 96 | { 97 | $this->navigationSort = $sort; 98 | 99 | return $this; 100 | } 101 | 102 | public function slug(string | Closure | null $slug): static 103 | { 104 | $this->slug = $slug; 105 | 106 | return $this; 107 | } 108 | 109 | public function registerNavigation(bool | Closure $shouldRegisterNavigation): static 110 | { 111 | $this->shouldRegisterNavigation = $shouldRegisterNavigation; 112 | 113 | return $this; 114 | } 115 | 116 | public function subNavigationPosition(SubNavigationPosition | Closure $subNavigationPosition): static 117 | { 118 | $this->subNavigationPosition = $subNavigationPosition; 119 | 120 | return $this; 121 | } 122 | 123 | // Getters 124 | public function getSubNavigationPosition(): SubNavigationPosition 125 | { 126 | return $this->evaluate($this->subNavigationPosition); 127 | } 128 | 129 | public function shouldEnableNavigationBadge(): bool 130 | { 131 | return $this->evaluate($this->shouldEnableNavigationBadge); 132 | } 133 | 134 | public function getNavigationBadgeColor(): string | array | null 135 | { 136 | return $this->evaluate($this->navigationBadgeColor); 137 | } 138 | 139 | public function getNavigationGroup(): ?string 140 | { 141 | return $this->evaluate($this->navigationGroup ?? __('filament-exceptions::filament-exceptions.labels.navigation_group')); 142 | } 143 | 144 | public function getNavigationParentItem(): ?string 145 | { 146 | return $this->evaluate($this->navigationParentItem); 147 | } 148 | 149 | public function getNavigationIcon(): string 150 | { 151 | return $this->evaluate($this->navigationIcon ?? 'heroicon-o-bug-ant'); 152 | } 153 | 154 | public function getActiveNavigationIcon(): ?string 155 | { 156 | return $this->evaluate($this->activeNavigationIcon ?? 'heroicon-s-bug-ant'); 157 | } 158 | 159 | public function getNavigationLabel(): string 160 | { 161 | return (string) $this->evaluate($this->navigationLabel ?? __('filament-exceptions::filament-exceptions.labels.navigation')); 162 | } 163 | 164 | public function getNavigationSort(): ?int 165 | { 166 | return $this->evaluate($this->navigationSort); 167 | } 168 | 169 | public function shouldRegisterNavigation(): bool 170 | { 171 | return $this->evaluate($this->shouldRegisterNavigation); 172 | } 173 | 174 | public function getSlug(): ?string 175 | { 176 | return $this->evaluate($this->slug); 177 | } 178 | 179 | /** 180 | * @return class-string | null 181 | */ 182 | public function getCluster(): ?string 183 | { 184 | return $this->evaluate($this->cluster); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/Concerns/HasTabs.php: -------------------------------------------------------------------------------- 1 | activeTab = $tab; 37 | 38 | return $this; 39 | } 40 | 41 | public function bodyTabIcon(string $icon): static 42 | { 43 | $this->bodyTabIcon = $icon; 44 | 45 | return $this; 46 | } 47 | 48 | public function bodyTabLabel(string $label): static 49 | { 50 | $this->bodyTabLabel = $label; 51 | 52 | return $this; 53 | } 54 | 55 | public function cookiesTabIcon(string $icon): static 56 | { 57 | $this->cookiesTabIcon = $icon; 58 | 59 | return $this; 60 | } 61 | 62 | public function cookiesTabLabel(string $label): static 63 | { 64 | $this->cookiesTabLabel = $label; 65 | 66 | return $this; 67 | } 68 | 69 | public function exceptionTabIcon(string $icon): static 70 | { 71 | $this->exceptionTabIcon = $icon; 72 | 73 | return $this; 74 | } 75 | 76 | public function exceptionTabLabel(string $label): static 77 | { 78 | $this->exceptionTabLabel = $label; 79 | 80 | return $this; 81 | } 82 | 83 | public function headersTabIcon(string $icon): static 84 | { 85 | $this->headersTabIcon = $icon; 86 | 87 | return $this; 88 | } 89 | 90 | public function headersTabLabel(string $label): static 91 | { 92 | $this->headersTabLabel = $label; 93 | 94 | return $this; 95 | } 96 | 97 | public function queriesTabIcon(string $icon): static 98 | { 99 | $this->queriesTabIcon = $icon; 100 | 101 | return $this; 102 | } 103 | 104 | public function queriesTabLabel(string $label): static 105 | { 106 | $this->queriesTabLabel = $label; 107 | 108 | return $this; 109 | } 110 | 111 | public function getActiveTab(): int 112 | { 113 | return $this->activeTab; 114 | } 115 | 116 | public function getBodyTabIcon(): string 117 | { 118 | return $this->bodyTabIcon ?? 'heroicon-o-code-bracket'; 119 | } 120 | 121 | public function getBodyTabLabel(): string 122 | { 123 | return $this->bodyTabLabel ?? __('filament-exceptions::filament-exceptions.labels.tabs.body'); 124 | } 125 | 126 | public function getCookiesTabIcon(): string 127 | { 128 | return $this->cookiesTabIcon ?? 'heroicon-o-chart-pie'; 129 | } 130 | 131 | public function getCookiesTabLabel(): string 132 | { 133 | return $this->cookiesTabLabel ?? __('filament-exceptions::filament-exceptions.labels.tabs.cookies'); 134 | } 135 | 136 | public function getExceptionTabIcon(): string 137 | { 138 | return $this->exceptionTabIcon ?? 'heroicon-o-bug-ant'; 139 | } 140 | 141 | public function getExceptionTabLabel(): string 142 | { 143 | return $this->exceptionTabLabel ?? __('filament-exceptions::filament-exceptions.labels.tabs.exception'); 144 | } 145 | 146 | public function getHeadersTabIcon(): string 147 | { 148 | return $this->headersTabIcon ?? 'heroicon-o-arrows-right-left'; 149 | } 150 | 151 | public function getHeadersTabLabel(): string 152 | { 153 | return $this->headersTabLabel ?? __('filament-exceptions::filament-exceptions.labels.tabs.headers'); 154 | } 155 | 156 | public function getQueriesTabIcon(): string 157 | { 158 | return $this->queriesTabIcon ?? 'heroicon-o-circle-stack'; 159 | } 160 | 161 | public function getQueriesTabLabel(): string 162 | { 163 | return $this->queriesTabLabel ?? __('filament-exceptions::filament-exceptions.labels.tabs.queries'); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/Concerns/HasTenantScope.php: -------------------------------------------------------------------------------- 1 | isScopedToTenant = $condition; 20 | 21 | return $this; 22 | } 23 | 24 | public function tenantOwnershipRelationshipName(string | Closure | null $ownershipRelationshipName): static 25 | { 26 | $this->tenantOwnershipRelationshipName = $ownershipRelationshipName; 27 | 28 | return $this; 29 | } 30 | 31 | public function tenantRelationshipName(string | Closure | null $relationshipName): static 32 | { 33 | $this->tenantRelationshipName = $relationshipName; 34 | 35 | return $this; 36 | } 37 | 38 | public function isScopedToTenant(): bool 39 | { 40 | return $this->evaluate($this->isScopedToTenant); 41 | } 42 | 43 | public function getTenantRelationshipName(): ?string 44 | { 45 | return $this->evaluate($this->tenantRelationshipName); 46 | } 47 | 48 | public function getTenantOwnershipRelationshipName(): ?string 49 | { 50 | return $this->evaluate($this->tenantOwnershipRelationshipName); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Facades/FilamentExceptions.php: -------------------------------------------------------------------------------- 1 | | null */ 17 | protected static ?string $cluster = null; 18 | 19 | public function __construct( 20 | protected Request $request 21 | ) {} 22 | 23 | /** 24 | * @throws Throwable 25 | */ 26 | public static function report(Throwable $exception): void 27 | { 28 | $reporter = new static(request()); 29 | 30 | $reporter->reportException($exception); 31 | } 32 | 33 | public static function cluster(string $cluster): void 34 | { 35 | static::$cluster = $cluster; 36 | } 37 | 38 | public static function getCluster(): ?string 39 | { 40 | return static::$cluster; 41 | } 42 | 43 | public static function getModel(): ?string 44 | { 45 | return static::$model; 46 | } 47 | 48 | public static function model(string $model): void 49 | { 50 | static::$model = $model; 51 | } 52 | 53 | /** 54 | * @throws BindingResolutionException 55 | * @throws Throwable 56 | */ 57 | public function reportException(Throwable $exception): void 58 | { 59 | $data = [ 60 | 'method' => request()->getMethod(), 61 | 'ip' => implode(' ', json_decode(json_encode(request()->getClientIps()))), 62 | 'path' => request()->path(), 63 | 'query' => app()->make(QueryRecorder::class)->getQueries(), 64 | 'body' => request()->getContent(), 65 | 'cookies' => request()->cookies->all(), 66 | 'headers' => Arr::except(request()->headers->all(), 'cookie'), 67 | 68 | 'type' => get_class($exception), 69 | 'code' => $exception->getCode(), 70 | 'file' => $exception->getFile(), 71 | 'line' => $exception->getLine(), 72 | 'message' => $exception->getMessage(), 73 | 'trace' => $exception->getTraceAsString(), 74 | ]; 75 | 76 | $data = $this->stringify($data); 77 | 78 | $this->store($data); 79 | } 80 | 81 | public function stringify($data): array 82 | { 83 | return array_map(function ($item) { 84 | return is_array($item) ? json_encode($item, JSON_OBJECT_AS_ARRAY) : (string) $item; 85 | }, $data); 86 | } 87 | 88 | public function store(array $data): bool 89 | { 90 | try { 91 | static::getModel()::create($data); 92 | 93 | return true; 94 | } catch (Throwable $e) { 95 | return false; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/FilamentExceptionsPlugin.php: -------------------------------------------------------------------------------- 1 | resources([ 39 | ExceptionResource::class, 40 | ]); 41 | 42 | } 43 | 44 | public function boot(Panel $panel): void {} 45 | 46 | public static function get(): static 47 | { 48 | /** @var static $plugin */ 49 | $plugin = filament(app(static::class)->getId()); 50 | 51 | return $plugin; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/FilamentExceptionsServiceProvider.php: -------------------------------------------------------------------------------- 1 | name('filament-exceptions') 19 | ->hasViews() 20 | ->hasTranslations() 21 | ->hasMigration('create_filament_exceptions_table') 22 | ->hasCommand(Commands\InstallCommand::class); 23 | } 24 | 25 | public function packageRegistered(): void 26 | { 27 | parent::packageRegistered(); 28 | 29 | $this->app->scoped('filament-exceptions', function ($app): FilamentExceptions { 30 | return new FilamentExceptions($app->make(Request::class)); 31 | }); 32 | 33 | } 34 | 35 | public function packageBooted(): void 36 | { 37 | parent::packageBooted(); 38 | 39 | FilamentAsset::register([ 40 | Js::make('filament-exceptions', __DIR__ . '/../resources/dist/filament-exceptions.js'), 41 | Css::make('filament-exceptions', __DIR__ . '/../resources/dist/filament-exceptions.css'), 42 | ], 'bezhansalleh/filament-exceptions'); 43 | 44 | $this->callAfterResolving(Schedule::class, function (Schedule $schedule) { 45 | $schedule->command('model:prune', [ 46 | '--model' => [FilamentExceptions::getModel()], 47 | ])->daily(); 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Models/Exception.php: -------------------------------------------------------------------------------- 1 | getModelPruneInterval()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/QueryRecorder/Query.php: -------------------------------------------------------------------------------- 1 | |null */ 26 | protected ?array $bindings; 27 | 28 | protected float $microtime; 29 | 30 | public static function fromQueryExecutedEvent(QueryExecuted $queryExecuted, bool $reportBindings = false): self 31 | { 32 | return new self( 33 | $queryExecuted->sql, 34 | $queryExecuted->time, 35 | /** @phpstan-ignore-next-line */ 36 | $queryExecuted->connectionName ?? '', 37 | $reportBindings ? $queryExecuted->bindings : null 38 | ); 39 | } 40 | 41 | /** 42 | * @param array|null $bindings 43 | */ 44 | protected function __construct( 45 | string $sql, 46 | float $time, 47 | string $connectionName, 48 | ?array $bindings = null, 49 | ?float $microtime = null 50 | ) { 51 | $this->sql = $sql; 52 | $this->time = $time; 53 | $this->connectionName = $connectionName; 54 | $this->bindings = $bindings; 55 | $this->microtime = $microtime ?? microtime(true); 56 | } 57 | 58 | /** 59 | * @return array 60 | */ 61 | public function toArray(): array 62 | { 63 | return [ 64 | 'sql' => $this->sql, 65 | 'time' => $this->time, 66 | 'connection_name' => $this->connectionName, 67 | 'bindings' => $this->bindings, 68 | 'microtime' => $this->microtime, 69 | ]; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/QueryRecorder/QueryRecorder.php: -------------------------------------------------------------------------------- 1 | app = $app; 35 | $this->reportBindings = $reportBindings; 36 | $this->maxQueries = $maxQueries; 37 | } 38 | 39 | public function start(): self 40 | { 41 | /** @phpstan-ignore-next-line */ 42 | $this->app['events']->listen(QueryExecuted::class, [$this, 'record']); 43 | 44 | return $this; 45 | } 46 | 47 | public function record(QueryExecuted $queryExecuted): void 48 | { 49 | $this->queries[] = Query::fromQueryExecutedEvent($queryExecuted, $this->reportBindings); 50 | 51 | if (is_int($this->maxQueries)) { 52 | $this->queries = array_slice($this->queries, -$this->maxQueries); 53 | } 54 | } 55 | 56 | /** 57 | * @return array> 58 | */ 59 | public function getQueries(): array 60 | { 61 | $queries = []; 62 | 63 | foreach ($this->queries as $query) { 64 | $queries[] = $query->toArray(); 65 | } 66 | 67 | return $queries; 68 | } 69 | 70 | public function reset(): void 71 | { 72 | $this->queries = []; 73 | } 74 | 75 | public function getReportBindings(): bool 76 | { 77 | return $this->reportBindings; 78 | } 79 | 80 | public function setReportBindings(bool $reportBindings): self 81 | { 82 | $this->reportBindings = $reportBindings; 83 | 84 | return $this; 85 | } 86 | 87 | public function getMaxQueries(): ?int 88 | { 89 | return $this->maxQueries; 90 | } 91 | 92 | public function setMaxQueries(?int $maxQueries): self 93 | { 94 | $this->maxQueries = $maxQueries; 95 | 96 | return $this; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Resources/ExceptionResource.php: -------------------------------------------------------------------------------- 1 | getModelLabel(); 35 | } 36 | 37 | public static function getPluralModelLabel(): string 38 | { 39 | return static::getPlugin()->getPluralModelLabel(); 40 | } 41 | 42 | public static function getActiveNavigationIcon(): ?string 43 | { 44 | return static::getPlugin()->getActiveNavigationIcon(); 45 | } 46 | 47 | public static function getNavigationGroup(): ?string 48 | { 49 | return static::getPlugin()->getNavigationGroup() ?? static::getTitleCasePluralModelLabel(); 50 | } 51 | 52 | public static function getNavigationLabel(): string 53 | { 54 | return static::getPlugin()->getNavigationLabel() ?? __('filament-exceptions::filament-exceptions.labels.navigation'); 55 | } 56 | 57 | public static function getNavigationIcon(): string 58 | { 59 | return static::getPlugin()->getNavigationIcon(); 60 | } 61 | 62 | public static function getSlug(): string 63 | { 64 | return static::getPlugin()->getSlug() ?? parent::getSlug(); 65 | } 66 | 67 | public static function getNavigationBadge(): ?string 68 | { 69 | return static::getPlugin()->shouldEnableNavigationBadge() 70 | ? static::getEloquentQuery()->count() 71 | : null; 72 | } 73 | 74 | public static function shouldRegisterNavigation(): bool 75 | { 76 | return filled(FilamentExceptions::getCluster()) || static::getPlugin()->shouldRegisterNavigation(); 77 | } 78 | 79 | public static function getNavigationSort(): ?int 80 | { 81 | return static::getPlugin()->getNavigationSort(); 82 | } 83 | 84 | public static function isScopedToTenant(): bool 85 | { 86 | return static::getPlugin()->isScopedToTenant(); 87 | } 88 | 89 | public static function getTenantRelationshipName(): string 90 | { 91 | return static::getPlugin()->getTenantRelationshipName() ?? parent::getTenantRelationshipName(); 92 | } 93 | 94 | public static function getTenantOwnershipRelationshipName(): string 95 | { 96 | return static::getPlugin()->getTenantOwnershipRelationshipName() ?? parent::getTenantOwnershipRelationshipName(); 97 | } 98 | 99 | public static function canGloballySearch(): bool 100 | { 101 | return static::getPlugin()->canGloballySearch() && parent::canGloballySearch(); 102 | } 103 | 104 | public static function form(Form $form): Form 105 | { 106 | return $form 107 | ->schema([ 108 | Forms\Components\Tabs::make('Heading') 109 | ->activeTab(static fn (): int => static::getPlugin()->getActiveTab()) 110 | ->tabs([ 111 | Forms\Components\Tabs\Tab::make('Exception') 112 | ->label(static fn (): string => static::getPlugin()->getExceptionTabLabel()) 113 | ->icon(static fn (): string => static::getPlugin()->getExceptionTabIcon()) 114 | ->schema([ 115 | Forms\Components\View::make('filament-exceptions::exception'), 116 | ]), 117 | Forms\Components\Tabs\Tab::make('Headers') 118 | ->label(static fn (): string => static::getPlugin()->getHeadersTabLabel()) 119 | ->icon(static fn (): string => static::getPlugin()->getHeadersTabIcon()) 120 | ->schema([ 121 | Forms\Components\View::make('filament-exceptions::headers'), 122 | ])->columns(1), 123 | Forms\Components\Tabs\Tab::make('Cookies') 124 | ->label(static fn (): string => static::getPlugin()->getCookiesTabLabel()) 125 | ->icon(static fn (): string => static::getPlugin()->getCookiesTabIcon()) 126 | ->schema([ 127 | Forms\Components\View::make('filament-exceptions::cookies'), 128 | ]), 129 | Forms\Components\Tabs\Tab::make('Body') 130 | ->label(static fn (): string => static::getPlugin()->getBodyTabLabel()) 131 | ->icon(static fn (): string => static::getPlugin()->getBodyTabIcon()) 132 | ->schema([ 133 | Forms\Components\View::make('filament-exceptions::body'), 134 | ]), 135 | Forms\Components\Tabs\Tab::make('Queries') 136 | ->label(static fn (): string => static::getPlugin()->getQueriesTabLabel()) 137 | ->icon(static fn (): string => static::getPlugin()->getQueriesTabIcon()) 138 | ->badge(static fn ($record): string => collect(json_decode($record->query, true, 512, JSON_THROW_ON_ERROR))->count()) 139 | ->schema([ 140 | Forms\Components\View::make('filament-exceptions::query'), 141 | ]), 142 | 143 | ]), 144 | ])->columns(1); 145 | } 146 | 147 | public static function table(Table $table): Table 148 | { 149 | return $table 150 | ->modifyQueryUsing(fn (Builder $query) => $query->select('id', 'path', 'method', 'type', 'code', 'ip', 'created_at')) 151 | ->columns([ 152 | Tables\Columns\TextColumn::make('method') 153 | ->label(fn (): string => __('filament-exceptions::filament-exceptions.columns.method')) 154 | ->badge() 155 | ->colors([ 156 | 'gray', 157 | 'success' => fn ($state): bool => $state === 'GET', 158 | 'primary' => fn ($state): bool => $state === 'POST', 159 | 'warning' => fn ($state): bool => $state === 'PUT', 160 | 'danger' => fn ($state): bool => $state === 'DELETE', 161 | 'warning' => fn ($state): bool => $state === 'PATCH', 162 | 'gray' => fn ($state): bool => $state === 'OPTIONS', 163 | 164 | ]) 165 | ->searchable() 166 | ->sortable(), 167 | Tables\Columns\TextColumn::make('path') 168 | ->label(fn (): string => __('filament-exceptions::filament-exceptions.columns.path')) 169 | ->searchable(), 170 | Tables\Columns\TextColumn::make('type') 171 | ->label(fn (): string => __('filament-exceptions::filament-exceptions.columns.type')) 172 | ->sortable() 173 | ->searchable(), 174 | Tables\Columns\TextColumn::make('code') 175 | ->label(fn (): string => __('filament-exceptions::filament-exceptions.columns.code')) 176 | ->searchable() 177 | ->sortable() 178 | ->toggleable(isToggledHiddenByDefault: false), 179 | Tables\Columns\TextColumn::make('ip') 180 | ->label(fn (): string => __('filament-exceptions::filament-exceptions.columns.ip')) 181 | ->badge() 182 | ->extraAttributes(['class' => 'font-mono']) 183 | ->sortable() 184 | ->searchable() 185 | ->toggleable(isToggledHiddenByDefault: false), 186 | Tables\Columns\TextColumn::make('created_at') 187 | ->label(fn (): string => __('filament-exceptions::filament-exceptions.columns.occurred_at')) 188 | ->sortable() 189 | ->searchable() 190 | ->dateTime() 191 | ->toggleable(isToggledHiddenByDefault: true), 192 | ]) 193 | ->filters([ 194 | // 195 | ]) 196 | ->actions([ 197 | Tables\Actions\ViewAction::make() 198 | ->color('primary'), 199 | ]) 200 | ->bulkActions([ 201 | Tables\Actions\BulkActionGroup::make([ 202 | Tables\Actions\DeleteBulkAction::make(), 203 | ]), 204 | ]) 205 | ->defaultSort('created_at', 'desc'); 206 | } 207 | 208 | public static function getRelations(): array 209 | { 210 | return [ 211 | // 212 | ]; 213 | } 214 | 215 | public static function getPages(): array 216 | { 217 | return [ 218 | 'index' => Pages\ListExceptions::route('/'), 219 | 'view' => Pages\ViewException::route('/{record}'), 220 | ]; 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/Resources/ExceptionResource/Pages/ListExceptions.php: -------------------------------------------------------------------------------- 1 | record->file}({$this->record->line})\n"; 19 | $frames = (new Parser($trace . $this->record->trace))->parse(); 20 | array_pop($frames); 21 | 22 | return $frames; 23 | } 24 | 25 | protected function getActions(): array 26 | { 27 | return [ 28 | Actions\DeleteAction::make(), 29 | ]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Trace/CodeBlock.php: -------------------------------------------------------------------------------- 1 | startLine = $startLine; 18 | $this->line = $line; 19 | $this->prefix = $prefix; 20 | $this->suffix = $suffix; 21 | } 22 | 23 | public function getStartLine() 24 | { 25 | return $this->startLine; 26 | } 27 | 28 | public function getLine() 29 | { 30 | return $this->line; 31 | } 32 | 33 | public function getSuffix() 34 | { 35 | return $this->suffix; 36 | } 37 | 38 | public function getPrefix() 39 | { 40 | return $this->prefix; 41 | } 42 | 43 | public function output(): string 44 | { 45 | return htmlentities($this->prefix . $this->line . $this->suffix); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Trace/Frame.php: -------------------------------------------------------------------------------- 1 | extract(); 20 | } 21 | 22 | public function extract(): ?array 23 | { 24 | preg_match('/#\d+\s([^:]+):?\s?(.*)/', $this->frame, $matches); 25 | $this->parseFileAndLine($matches[1]); 26 | $this->parseCall($matches[2]); 27 | $this->fetchCodeBlock(); 28 | 29 | return $this->attributes; 30 | } 31 | 32 | public function parseFileAndLine($str): void 33 | { 34 | if (str()->startsWith($str, '/')) { 35 | preg_match('/^([^(]+)\((\d+)\)/', $str, $matches); 36 | [, $this->attributes['file'], $this->attributes['line']] = $matches; 37 | } else { 38 | $this->attributes['name'] = $str; 39 | } 40 | } 41 | 42 | public function parseCall($str): void 43 | { 44 | if (empty($str)) { 45 | return; 46 | } 47 | if (preg_match('/^[^(]+(->|::)/', $str, $m)) { 48 | preg_match('/([^:-]+)(?:->|::)([^(]+)\((.*)\)/', $str, $matches); 49 | $this->attributes['class'] = $matches[1]; 50 | $this->attributes['method'] = $matches[2]; 51 | $this->attributes['args'] = $this->extractArgs($matches[3]); 52 | if (str()->contains($matches[2], ['{closure}']) && Arr::get($this->attributes, 'name') == '[internal function]') { 53 | $this->attributes['name'] .= " $matches[1]->$matches[2]"; 54 | } 55 | // class method call 56 | } else { 57 | preg_match('/([^(]+)\((.*)\)/', $str, $matches); 58 | $this->attributes['function'] = $matches[1]; 59 | $this->attributes['args'] = $this->extractArgs($matches[2]); 60 | } 61 | } 62 | 63 | public function fetchCodeBlock(): void 64 | { 65 | $filename = Arr::get($this->attributes, 'file'); 66 | $lineNo = Arr::get($this->attributes, 'line'); 67 | $class = Arr::get($this->attributes, 'class'); 68 | $method = Arr::get($this->attributes, 'method'); 69 | if ((! $filename || ! $lineNo) && ($class && $method)) { 70 | if (! class_exists($class)) { 71 | return; 72 | } 73 | $classReflection = new ReflectionClass($class); 74 | $filename = $classReflection->getFileName(); 75 | if (! $classReflection->hasMethod($method)) { 76 | return; 77 | } 78 | $methodReflection = $classReflection->getMethod($method); 79 | 80 | $lineNo = $methodReflection->getStartLine(); 81 | } 82 | if (! $filename || ! $lineNo) { 83 | return; 84 | } 85 | 86 | try { 87 | $file = new SplFileObject($filename); 88 | $target = max(0, ($lineNo - (5 + 1))); 89 | $file->seek($target); 90 | 91 | $curLineNo = $target + 1; 92 | $line = $prefix = $suffix = ''; 93 | 94 | while (! $file->eof()) { 95 | if ($curLineNo == $lineNo) { 96 | $line .= $file->current(); 97 | } elseif ($curLineNo < $lineNo) { 98 | $prefix .= $file->current(); 99 | } elseif ($curLineNo > $lineNo) { 100 | $suffix .= $file->current(); 101 | } 102 | $curLineNo++; 103 | if ($curLineNo > $lineNo + 5) { 104 | break; 105 | } 106 | $file->next(); 107 | } 108 | $this->code = new CodeBlock($target + 1, $line, $prefix, $suffix); 109 | $this->attributes['file'] = $filename; 110 | $this->attributes['line'] = $lineNo; 111 | } catch (RuntimeException) { 112 | return; 113 | } 114 | } 115 | 116 | public function getCodeBlock(): array | CodeBlock 117 | { 118 | return ! empty($this->code) ? $this->code : new CodeBlock; 119 | } 120 | 121 | public function method() 122 | { 123 | return Arr::get($this->attributes, 'method', Arr::get($this->attributes, 'function', '')); 124 | } 125 | 126 | /** 127 | * @throws ReflectionException 128 | */ 129 | public function args(): array 130 | { 131 | if (empty($this->attributes['args'])) { 132 | return []; 133 | } 134 | $args = []; 135 | $names = $this->getParameterNames(); 136 | foreach ($this->attributes['args'] as $key => $val) { 137 | $args[Arr::get($names, $key, "param$key")] = $val; 138 | } 139 | 140 | return $args; 141 | } 142 | 143 | /** 144 | * @throws ReflectionException 145 | */ 146 | public function getParameterNames(): array 147 | { 148 | $names = []; 149 | $class = Arr::get($this->attributes, 'class'); 150 | $method = Arr::get($this->attributes, 'method'); 151 | if (class_exists($class) && isset($method)) { 152 | $classReflection = new ReflectionClass($class); 153 | if (! $classReflection->hasMethod($method)) { 154 | return $names; 155 | } 156 | foreach ($classReflection->getMethod($method)->getParameters() as $reflection) { 157 | $names[] = $reflection->getName(); 158 | } 159 | } 160 | 161 | return $names; 162 | } 163 | 164 | protected function extractArgs($args): array 165 | { 166 | if (empty($args)) { 167 | return []; 168 | } 169 | $args = explode(',', $args); 170 | 171 | return array_map('trim', $args); 172 | } 173 | 174 | public function line() 175 | { 176 | return Arr::get($this->attributes, 'line', 0); 177 | } 178 | 179 | public function __call($method, $arguments = []) 180 | { 181 | return Arr::get($this->attributes, $method, ''); 182 | } 183 | 184 | public function __get($key) 185 | { 186 | return Arr::get($this->attributes, $key, ''); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/Trace/Parser.php: -------------------------------------------------------------------------------- 1 | trace); 16 | 17 | return collect($frames)->map(function ($frame) { 18 | return new Frame($frame); 19 | })->toArray(); 20 | } 21 | 22 | /** 23 | * {@inheritDoc} 24 | */ 25 | #[\ReturnTypeWillChange] 26 | public function current() 27 | { 28 | // TODO: Implement current() method. 29 | } 30 | 31 | /** 32 | * {@inheritDoc} 33 | */ 34 | #[\ReturnTypeWillChange] 35 | public function next() 36 | { 37 | // TODO: Implement next() method. 38 | } 39 | 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | #[\ReturnTypeWillChange] 44 | public function key() 45 | { 46 | // TODO: Implement key() method. 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | #[\ReturnTypeWillChange] 53 | public function valid() 54 | { 55 | // TODO: Implement valid() method. 56 | } 57 | 58 | /** 59 | * {@inheritDoc} 60 | */ 61 | #[\ReturnTypeWillChange] 62 | public function rewind() 63 | { 64 | // TODO: Implement rewind() method. 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | const preset = require("./vendor/filament/filament/tailwind.config.preset"); 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | module.exports = { 5 | presets: [preset], 6 | content: ["./resources/views/**/*.blade.php", "./src/**/*.php"], 7 | darkMode: "class", 8 | theme: { 9 | extend: {}, 10 | }, 11 | }; 12 | --------------------------------------------------------------------------------