├── .gitattributes ├── .github └── workflows │ └── run-tests.yml ├── .gitignore ├── composer.json ├── config └── telescope-toolbar.php ├── license.md ├── phpunit.xml.dist ├── readme.md ├── resources ├── css │ ├── base.css │ ├── custom.css │ └── theme_light.css ├── icons │ ├── LICENSE.txt │ ├── ajax.svg │ ├── cache.svg │ ├── close.svg │ ├── commands.svg │ ├── config.svg │ ├── dumps.svg │ ├── events.svg │ ├── exceptions.svg │ ├── forward.svg │ ├── gates.svg │ ├── http-client.svg │ ├── jobs.svg │ ├── laravel.svg │ ├── logs.svg │ ├── mail.svg │ ├── memory.svg │ ├── menu.svg │ ├── models.svg │ ├── notifications.svg │ ├── queries.svg │ ├── redirect.svg │ ├── redis.svg │ ├── refresh.svg │ ├── requests.svg │ ├── schedule.svg │ ├── search.svg │ ├── session.svg │ ├── telescope.svg │ ├── time.svg │ ├── user.svg │ ├── validator.svg │ └── views.svg └── views │ ├── base_js.blade.php │ ├── collectors │ ├── ajax.blade.php │ ├── cache.blade.php │ ├── commands.blade.php │ ├── config.blade.php │ ├── dumps.blade.php │ ├── events.blade.php │ ├── exceptions.blade.php │ ├── gates.blade.php │ ├── jobs.blade.php │ ├── logs.blade.php │ ├── mail.blade.php │ ├── memory.blade.php │ ├── models.blade.php │ ├── notifications.blade.php │ ├── queries.blade.php │ ├── redis.blade.php │ ├── request.blade.php │ ├── session.blade.php │ ├── time.blade.php │ ├── user.blade.php │ └── views.blade.php │ ├── head.blade.php │ ├── item.blade.php │ ├── toolbar.blade.php │ └── widget.blade.php ├── src ├── Http │ ├── Controllers │ │ └── ToolbarController.php │ ├── assets.php │ └── routes.php ├── Toolbar.php ├── ToolbarServiceProvider.php └── helpers.php └── tests ├── BrowserTestCase.php ├── TelescopeFeatureTestCase.php ├── TestCase.php ├── ToolbarBrowserTest.php ├── ToolbarTest.php └── resources └── views └── dashboard.blade.php /.gitattributes: -------------------------------------------------------------------------------- 1 | *.blade.php linguist-language=PHP -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - "*" 10 | 11 | jobs: 12 | php-tests: 13 | runs-on: ubuntu-latest 14 | 15 | timeout-minutes: 15 16 | 17 | env: 18 | COMPOSER_NO_INTERACTION: 1 19 | 20 | strategy: 21 | matrix: 22 | php: [8.4, 8.3, 8.2, 8.1, 8.0] 23 | laravel: ['9.*', '10.*', '11.*', '12.*'] 24 | dependency-version: [prefer-stable] 25 | exclude: 26 | - laravel: 10.* 27 | php: 8.0 28 | - laravel: 10.* 29 | php: 8.3 30 | - laravel: 10.* 31 | php: 8.4 32 | - laravel: 11.* 33 | php: 8.1 34 | - laravel: 11.* 35 | php: 8.0 36 | - laravel: 12.* 37 | php: 8.1 38 | - laravel: 12.* 39 | php: 8.0 40 | 41 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} 42 | 43 | steps: 44 | - name: Checkout code 45 | uses: actions/checkout@v2 46 | 47 | - name: Setup PHP 48 | uses: shivammathur/setup-php@v2 49 | with: 50 | php-version: ${{ matrix.php }} 51 | coverage: none 52 | tools: composer:v2 53 | 54 | - name: Install dependencies 55 | run: | 56 | composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update 57 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-progress 58 | 59 | - name: Update Dusk Chromedriver 60 | run: vendor/bin/dusk-updater detect --auto-update 61 | 62 | - name: Install Sqlite Database 63 | run: vendor/bin/testbench-dusk package:create-sqlite-db 64 | 65 | - name: Execute Unit Tests 66 | run: composer test 67 | 68 | - name: Upload Failed Screenshots 69 | uses: actions/upload-artifact@v4 70 | if: failure() 71 | with: 72 | name: screenshots 73 | path: tests/Browser/screenshots/* 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store 5 | .phpunit.result.cache 6 | /tests/Browser 7 | /build -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fruitcake/laravel-telescope-toolbar", 3 | "description": "Toolbar for Laravel Telescope based on Symfony Web Profiler", 4 | "keywords": [ 5 | "laravel", 6 | "telescope", 7 | "toolbar", 8 | "debugbar", 9 | "profiler", 10 | "debug", 11 | "webprofiler" 12 | ], 13 | "license": "MIT", 14 | "authors": [ 15 | { 16 | "name": "Fruitcake", 17 | "email": "info@fruitcake.nl" 18 | }, 19 | { 20 | "name": "Barry vd. Heuvel", 21 | "email": "barryvdh@gmail.com" 22 | } 23 | ], 24 | "require": { 25 | "php": "^8", 26 | "ext-json": "*", 27 | "laravel/framework": "^9|^10|^11|^12", 28 | "laravel/telescope": "^4|^5" 29 | }, 30 | "require-dev": { 31 | "orchestra/testbench-dusk": "^6|^7|^8|^9|^10" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "Fruitcake\\TelescopeToolbar\\": "src/" 36 | }, 37 | "files": [ 38 | "src/helpers.php" 39 | ] 40 | }, 41 | "autoload-dev": { 42 | "psr-4": { 43 | "Fruitcake\\TelescopeToolbar\\Tests\\": "tests" 44 | } 45 | }, 46 | "minimum-stability": "dev", 47 | "prefer-stable": true, 48 | "extra": { 49 | "branch-alias": { 50 | "dev-master": "1.4-dev" 51 | }, 52 | "laravel": { 53 | "providers": [ 54 | "Fruitcake\\TelescopeToolbar\\ToolbarServiceProvider" 55 | ], 56 | "aliases": { 57 | "Toolbar": "Fruitcake\\TelescopeToolbar\\Toolbar" 58 | } 59 | } 60 | }, 61 | "scripts": { 62 | "test": "phpunit" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /config/telescope-toolbar.php: -------------------------------------------------------------------------------- 1 | env('TELESCOPE_TOOLBAR_ENABLED', env('TELESCOPE_ENABLED', true)), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Telescope Auto Replace Requests 21 | |-------------------------------------------------------------------------- 22 | | 23 | | This options automatically replaces the content of the toolbar with new 24 | | (AJAX) requests. 25 | | 26 | */ 27 | 'replace' => env('TELESCOPE_TOOLBAR_REPLACE', true), 28 | 29 | /* 30 | |-------------------------------------------------------------------------- 31 | | Use Telescope Theme ('light mode') 32 | |-------------------------------------------------------------------------- 33 | | 34 | | This option enabled/disables the Light Theme. 35 | | Laravel Telescope toolbar has two themes; Light and Dark. 36 | | By default is uses dark theme. 37 | | Values be: 'auto', true, or false (default) 38 | | 39 | */ 40 | 'light_theme' => env('TELESCOPE_LIGHT_THEME', false), 41 | 42 | /* 43 | |-------------------------------------------------------------------------- 44 | | Open links in new tab 45 | |-------------------------------------------------------------------------- 46 | | 47 | | This option enabled/disables opening telescope links in a new tab. 48 | | By default, clicking on a tab will open telescope in the same tab. 49 | | Values be: true, false 50 | | 51 | */ 52 | 'new_tab' => env('TELESCOPE_TOOLBAR_NEW_TAB', false), 53 | 54 | /* 55 | |-------------------------------------------------------------------------- 56 | | Route path of Toolbar 57 | |-------------------------------------------------------------------------- 58 | | 59 | | The route path that is being used to collect the toolbar metrics. 60 | | 61 | */ 62 | 'path' => '_tt', 63 | 64 | /* 65 | |-------------------------------------------------------------------------- 66 | | Middleware of Toolbar 67 | |-------------------------------------------------------------------------- 68 | | 69 | | The middleware that is used for the Telescope API routes. By default 70 | | it will use the Telescope middleware. 71 | | 72 | */ 73 | 'middleware' => [ 74 | 'telescope' 75 | ], 76 | 77 | 'asset_middleware' => [ 78 | 'web' 79 | ], 80 | 81 | /* 82 | |-------------------------------------------------------------------------- 83 | | Excluded Ajax Paths 84 | |-------------------------------------------------------------------------- 85 | | 86 | | This Javascript RegExp excludes Ajax Requests from being collected. 87 | | 88 | */ 89 | 'excluded_ajax_paths' => '^/_tt|^/_debugbar|^/horizon', 90 | 91 | /* 92 | |-------------------------------------------------------------------------- 93 | | Ignored Paths 94 | |-------------------------------------------------------------------------- 95 | | 96 | | This is a list of paths the toolbar will not run on. 97 | | 98 | */ 99 | 'ignore_paths' => [ 100 | // 101 | ], 102 | 103 | /* 104 | |-------------------------------------------------------------------------- 105 | | Store Redirects in Session 106 | |-------------------------------------------------------------------------- 107 | | 108 | | This options stores Redirect responses in the Session to show 109 | | them in in the Requests tab on the next 'real' response. 110 | | 111 | */ 112 | 'store_redirects' => true, 113 | 114 | /* 115 | |-------------------------------------------------------------------------- 116 | | Enable Dump Watcher 117 | |-------------------------------------------------------------------------- 118 | | 119 | | This options listens for dumps always, without having the tab open. 120 | | You can specify the number of seconds it listens or disable with `false` 121 | | 122 | */ 123 | 'dump_watcher' => false, 124 | 125 | /* 126 | |-------------------------------------------------------------------------- 127 | | Collectors 128 | |-------------------------------------------------------------------------- 129 | | 130 | | This options configures which collectors are shown 131 | | 132 | */ 133 | 'collectors' => [ 134 | EntryType::REQUEST => [ 135 | 'telescope-toolbar::collectors.request', 136 | 'telescope-toolbar::collectors.time', 137 | 'telescope-toolbar::collectors.memory', 138 | 'telescope-toolbar::collectors.session', 139 | 'telescope-toolbar::collectors.user', 140 | ], 141 | EntryType::EXCEPTION => [ 142 | 'telescope-toolbar::collectors.exceptions', 143 | ], 144 | EntryType::VIEW => [ 145 | 'telescope-toolbar::collectors.views', 146 | ], 147 | EntryType::QUERY => [ 148 | 'telescope-toolbar::collectors.queries', 149 | ], 150 | EntryType::CACHE => [ 151 | 'telescope-toolbar::collectors.cache', 152 | ], 153 | EntryType::LOG => [ 154 | 'telescope-toolbar::collectors.logs', 155 | ], 156 | EntryType::MAIL => [ 157 | 'telescope-toolbar::collectors.mail', 158 | ], 159 | EntryType::NOTIFICATION => [ 160 | 'telescope-toolbar::collectors.notifications', 161 | ], 162 | EntryType::GATE => [ 163 | 'telescope-toolbar::collectors.gates', 164 | ], 165 | EntryType::JOB => [ 166 | 'telescope-toolbar::collectors.jobs', 167 | ], 168 | EntryType::COMMAND => [ 169 | 'telescope-toolbar::collectors.commands', 170 | ], 171 | EntryType::DUMP => [ 172 | 'telescope-toolbar::collectors.dumps', 173 | ], 174 | EntryType::EVENT => [ 175 | 'telescope-toolbar::collectors.events', 176 | ], 177 | EntryType::MODEL => [ 178 | 'telescope-toolbar::collectors.models', 179 | ], 180 | EntryType::REDIS => [ 181 | 'telescope-toolbar::collectors.redis', 182 | ], 183 | EntryType::SCHEDULED_TASK => [ 184 | 185 | ], 186 | ], 187 | 188 | ]; 189 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2019-present Fruitcake, Barry vd. Heuvel 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Laravel Telescope Toolbar 2 | ![Unit Tests](https://github.com/fruitcake/laravel-telescope-toolbar/workflows/Unit%20Tests/badge.svg) 3 | [![Packagist License](https://img.shields.io/badge/Licence-MIT-blue)](http://choosealicense.com/licenses/mit/) 4 | [![Latest Stable Version](https://img.shields.io/packagist/v/fruitcake/laravel-telescope-toolbar?label=Stable)](https://packagist.org/packages/fruitcake/laravel-telescope-toolbar) 5 | [![Total Downloads](https://img.shields.io/packagist/dt/fruitcake/laravel-telescope-toolbar?label=Downloads)](https://packagist.org/packages/fruitcake/laravel-telescope-toolbar) 6 | [![Fruitcake](https://img.shields.io/badge/Powered%20By-Fruitcake-b2bc35.svg)](https://fruitcake.nl/) 7 | 8 | 9 | ### Extends Laravel Telescope to show a powerful Toolbar 10 | See https://github.com/laravel/telescope 11 | 12 | #### Install 13 | 14 | First install Telescope and check it works (see https://laravel.com/docs/master/telescope) 15 | 16 | ```bash 17 | composer require laravel/telescope 18 | php artisan telescope:install 19 | 20 | # Telescope 5.0 no longer automatically loads migrations from its own migrations directory. Instead, you should run the following command to publish Telescope's migrations to your application: 21 | php artisan vendor:publish --tag=telescope-migrations 22 | 23 | php artisan migrate 24 | ``` 25 | 26 | Then just install the package with Composer and it will register automatically: 27 | 28 | ```bash 29 | composer require fruitcake/laravel-telescope-toolbar --dev 30 | ``` 31 | 32 | The Toolbar will show by default when Telescope is enabled and APP_DEBUG is true. 33 | It can also enabled or disabled using the `TELESCOPE_TOOLBAR_ENABLED` environment variable. 34 | 35 | ![image](https://user-images.githubusercontent.com/973269/63676710-d79ad000-c7eb-11e9-8793-c58c6bc25bbe.png) 36 | 37 | > Note: The Toolbar is intended for Development environments, not for production. 38 | 39 | ## Publishing the config 40 | 41 | Run this command to publish the config for this package: 42 | 43 | ```php 44 | php artisan vendor:publish --provider="Fruitcake\\TelescopeToolbar\\ToolbarServiceProvider" 45 | ``` 46 | 47 | #### Current Features 48 | 49 | - Inject Toolbar for quick info 50 | - Show redirects and Ajax Requests 51 | - Link to related Telescope Entry page 52 | - Show up to 5 entries for collectors, link to details 53 | - Supported Collectors: 54 | * Request info / timing 55 | * User auth 56 | * Database queries 57 | * Laravel/php version 58 | * Cache hit/miss/set 59 | * Logger entries 60 | * Exceptions 61 | * Mails 62 | * Notifications 63 | * Jobs 64 | * Dumps (when watching the Dump screen, or using `debug(...$args)`) 65 | * Number of entries for: Commands/Models/Events 66 | 67 | #### Screenshots 68 | 69 | Ajax/ Redirects stack: 70 | 71 | ![image](https://user-images.githubusercontent.com/973269/63675364-ef248980-c7e8-11e9-8696-dbddd9feb4bd.png) 72 | 73 | Preview for Exceptions/Mail/Notifications/Log entries with link to details: 74 | 75 | ![image](https://user-images.githubusercontent.com/973269/63676030-67d81580-c7ea-11e9-9526-129bec5187f9.png) 76 | 77 | Counter for Queries (and Cache etc): 78 | 79 | ![image](https://user-images.githubusercontent.com/973269/63675578-68bc7780-c7e9-11e9-915d-b955dd070c94.png) 80 | 81 | 82 | Catch `debug()`/`Toolbar::dump()` calls and show them directly in the Toolbar instead of the page: 83 | 84 | ![image](https://user-images.githubusercontent.com/973269/63676511-60653c00-c7eb-11e9-9b8e-9473964a29a8.png) 85 | 86 | ## Running the Test Suite 87 | 88 | - Make sure ChromeDriver is up to date: `vendor/bin/dusk-updater detect --auto-update` 89 | - Create the Sqlite database: `vendor/orchestra/testbench-dusk/create-sqlite-db` 90 | - Run the tests: `composer test` 91 | 92 | ## License and attribution 93 | 94 | Laravel Telescope Toolbar is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). 95 | 96 | ### Based on Symfony Web Profiler and Laravel Telescope 97 | The styling, javascript, some icons and html of the Toolbar and (part of) its Collectors are based on the Symfony Web Profiler. 98 | JS/CSS is mostly copied and converted to Blade syntax. Collectors are modified to show Laravel data. 99 | See https://github.com/symfony/web-profiler-bundle - © 2004-2019 Fabien Potencier 100 | 101 | Data from collectors is provided by Laravel Telescope. Some styling/icons/logic are alse re-used. 102 | See https://github.com/laravel/telescope - © Taylor Otwell 103 | 104 | -------------------------------------------------------------------------------- /resources/css/base.css: -------------------------------------------------------------------------------- 1 | .sf-toolbarreset { 2 | --sf-toolbar-font-family-system: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 3 | --sf-toolbar-font-family-monospace: "Ubuntu Mono", "JetBrains Mono", ui-monospace, "Roboto Mono", SFMono-Regular, Menlo, Monaco, Consolas,"Liberation Mono", "Courier New", monospace; 4 | 5 | --sf-toolbar-white: #fff; 6 | --sf-toolbar-black: #000; 7 | --sf-toolbar-gray-50: #fafafa; 8 | --sf-toolbar-gray-100: #f5f5f5; 9 | --sf-toolbar-gray-200: #e5e5e5; 10 | --sf-toolbar-gray-300: #d4d4d4; 11 | --sf-toolbar-gray-400: #a3a3a3; 12 | --sf-toolbar-gray-500: #737373; 13 | --sf-toolbar-gray-600: #525252; 14 | --sf-toolbar-gray-700: #404040; 15 | --sf-toolbar-gray-800: #262626; 16 | --sf-toolbar-gray-900: #171717; 17 | --sf-toolbar-red-50: #FEFBFC; 18 | --sf-toolbar-red-100: #FCE9ED; 19 | --sf-toolbar-red-200: #F5B8C5; 20 | --sf-toolbar-red-300: #EF869C; 21 | --sf-toolbar-red-400: #E85574; 22 | --sf-toolbar-red-500: #E1244B; 23 | --sf-toolbar-red-600: #B41939; 24 | --sf-toolbar-red-700: #83122A; 25 | --sf-toolbar-red-800: #510B1A; 26 | --sf-toolbar-red-900: #20040A; 27 | --sf-toolbar-yellow-50: #fef7e1; 28 | --sf-toolbar-yellow-100: #fef2cd; 29 | --sf-toolbar-yellow-200: #fde496; 30 | --sf-toolbar-yellow-300: #fcd55f; 31 | --sf-toolbar-yellow-400: #fbc728; 32 | --sf-toolbar-yellow-500: #e6af05; 33 | --sf-toolbar-yellow-600: #af8503; 34 | --sf-toolbar-yellow-700: #785b02; 35 | --sf-toolbar-yellow-800: #413101; 36 | --sf-toolbar-yellow-900: #0a0800; 37 | --sf-toolbar-green-50: #eff5f5; 38 | --sf-toolbar-green-100: #deeaea; 39 | --sf-toolbar-green-200: #bbd5d5; 40 | --sf-toolbar-green-300: #99bfbf; 41 | --sf-toolbar-green-400: #76a9a9; 42 | --sf-toolbar-green-500: #598e8e; 43 | --sf-toolbar-green-600: #436c6c; 44 | --sf-toolbar-green-700: #2e4949; 45 | --sf-toolbar-green-800: #182727; 46 | --sf-toolbar-green-900: #030404; 47 | } 48 | 49 | .sf-minitoolbar { 50 | background-color: var(--sf-toolbar-gray-800); 51 | border-top-left-radius: 4px; 52 | bottom: 0; 53 | box-sizing: border-box; 54 | display: none; 55 | height: 36px; 56 | padding: 6px; 57 | position: fixed; 58 | right: 0; 59 | z-index: 99999; 60 | } 61 | 62 | .sf-minitoolbar button { 63 | background-color: transparent; 64 | padding: 0; 65 | border: none; 66 | } 67 | .sf-minitoolbar svg, 68 | .sf-minitoolbar img { 69 | color: var(--sf-toolbar-gray-200); 70 | max-height: 24px; 71 | max-width: 24px; 72 | display: inline; 73 | } 74 | 75 | .sf-toolbar-clearer { 76 | clear: both; 77 | height: 36px; 78 | } 79 | 80 | .sf-display-none { 81 | display: none; 82 | } 83 | 84 | .sf-toolbarreset *:not(svg rect) { 85 | box-sizing: content-box; 86 | vertical-align: baseline; 87 | letter-spacing: normal; 88 | width: auto; 89 | } 90 | 91 | .sf-toolbarreset { 92 | background-color: var(--sf-toolbar-gray-800); 93 | bottom: 0; 94 | box-shadow: inset 0 1px 0 var(--sf-toolbar-black), 0 -1px 0 rgba(0, 0, 0, 0.5); 95 | color: var(--sf-toolbar-gray-200); 96 | font: 11px var(--sf-toolbar-font-family-system); 97 | left: 0; 98 | margin: 0; 99 | padding: 0 36px 0 0; 100 | position: fixed; 101 | right: 0; 102 | text-align: left; 103 | text-transform: none; 104 | z-index: 99999; 105 | direction: ltr; 106 | 107 | /* neutralize the aliasing defined by external CSS styles */ 108 | -webkit-font-smoothing: subpixel-antialiased; 109 | -moz-osx-font-smoothing: auto; 110 | } 111 | .sf-toolbarreset abbr { 112 | border: dashed var(--sf-toolbar-gray-500); 113 | border-width: 0 0 1px; 114 | } 115 | .sf-toolbarreset svg, 116 | .sf-toolbarreset img { 117 | height: 20px; 118 | width: 20px; 119 | display: inline-block; 120 | } 121 | 122 | .sf-toolbarreset .sf-cancel-button { 123 | color: var(--sf-toolbar-gray-700); 124 | } 125 | 126 | .sf-toolbarreset .hide-button { 127 | background: var(--sf-toolbar-gray-800); 128 | color: var(--sf-toolbar-gray-300); 129 | display: block; 130 | position: absolute; 131 | top: 2px; 132 | right: 0; 133 | width: 36px; 134 | height: 34px; 135 | cursor: pointer; 136 | text-align: center; 137 | border: none; 138 | margin: 0; 139 | padding: 0; 140 | } 141 | .sf-toolbarreset .hide-button:hover { 142 | background: var(--sf-toolbar-gray-700); 143 | } 144 | .sf-toolbarreset .hide-button svg { 145 | max-height: 18px; 146 | margin-top: 1px; 147 | } 148 | 149 | .sf-toolbar-block { 150 | cursor: default; 151 | display: block; 152 | float: left; 153 | height: 36px; 154 | margin-right: 0; 155 | position: relative; 156 | white-space: nowrap; 157 | max-width: 15%; 158 | } 159 | .sf-toolbar-block > a, 160 | .sf-toolbar-block > a:hover { 161 | display: block; 162 | text-decoration: none; 163 | background-color: transparent; 164 | color: inherit; 165 | } 166 | 167 | .sf-toolbar-block span { 168 | display: inline-block; 169 | } 170 | .sf-toolbar-block .sf-toolbar-value { 171 | color: var(--sf-toolbar-gray-100); 172 | font-size: 13px; 173 | line-height: 36px; 174 | padding: 0; 175 | } 176 | .sf-toolbar-block .sf-toolbar-label, 177 | .sf-toolbar-block .sf-toolbar-class-separator { 178 | color: var(--sf-toolbar-gray-400); 179 | font-size: 12px; 180 | margin-left: 2px; 181 | } 182 | 183 | .sf-toolbar-block .sf-toolbar-info { 184 | border-collapse: collapse; 185 | display: table; 186 | z-index: 100000; 187 | } 188 | .sf-toolbar-block hr { 189 | border-top: 1px solid var(--sf-toolbar-gray-500); 190 | margin: 4px 0; 191 | padding-top: 4px; 192 | } 193 | .sf-toolbar-block .sf-toolbar-info-piece { 194 | /* this 'border-bottom' trick is needed because 'margin-bottom' doesn't work for table rows */ 195 | border-bottom: solid transparent 3px; 196 | display: table-row; 197 | } 198 | .sf-toolbar-block .sf-toolbar-info-piece-additional, 199 | .sf-toolbar-block .sf-toolbar-info-piece-additional-detail { 200 | display: none; 201 | } 202 | .sf-toolbar-block .sf-toolbar-info-group { 203 | margin-bottom: 4px; 204 | padding-bottom: 2px; 205 | border-bottom: 1px solid #333333; 206 | } 207 | .sf-toolbar-block .sf-toolbar-info-group:last-child { 208 | margin-bottom: 0; 209 | padding-bottom: 0; 210 | border-bottom: none; 211 | } 212 | 213 | .sf-toolbar-block .sf-toolbar-info-piece .sf-toolbar-status { 214 | border-radius: 4px; 215 | padding: 2px 5px; 216 | margin-bottom: 0; 217 | } 218 | .sf-toolbar-block .sf-toolbar-info-piece .sf-toolbar-status + .sf-toolbar-status { 219 | margin-left: 4px; 220 | } 221 | 222 | .sf-toolbar-block .sf-toolbar-info-piece:last-child { 223 | margin-bottom: 0; 224 | } 225 | 226 | div.sf-toolbar .sf-toolbar-block .sf-toolbar-info-piece a { 227 | color: #99CDD8; 228 | text-decoration: underline; 229 | } 230 | div.sf-toolbar .sf-toolbar-block a:hover { 231 | text-decoration: none; 232 | } 233 | 234 | .sf-toolbar-block .sf-toolbar-info-piece b { 235 | color: var(--sf-toolbar-gray-400); 236 | display: table-cell; 237 | font-size: 11px; 238 | padding: 4px 8px 4px 0; 239 | } 240 | .sf-toolbar-block:not(.sf-toolbar-block-dump) .sf-toolbar-info-piece span { 241 | color: var(--sf-toolbar-gray-100); 242 | } 243 | .sf-toolbar-block .sf-toolbar-info-piece span { 244 | font-size: 12px; 245 | } 246 | div.sf-toolbar .sf-toolbar-block .sf-toolbar-info-piece.sf-toolbar-info-php-ext a { 247 | text-decoration: none; 248 | } 249 | 250 | .sf-toolbar-block .sf-toolbar-info { 251 | background-color: var(--sf-toolbar-gray-700); 252 | border-radius: 4px; 253 | border-bottom-left-radius: 0; 254 | bottom: 36px; 255 | color: var(--sf-toolbar-gray-100); 256 | display: none; 257 | padding: 9px 0; 258 | position: absolute; 259 | } 260 | 261 | .sf-toolbar-block.sf-toolbar-block-right .sf-toolbar-info { 262 | border-bottom-left-radius: 4px; 263 | border-bottom-right-radius: 0; 264 | } 265 | 266 | .sf-toolbar-block .sf-toolbar-info:empty { 267 | visibility: hidden; 268 | } 269 | 270 | .sf-toolbar-block .sf-toolbar-status { 271 | display: inline-block; 272 | color: var(--sf-toolbar-white); 273 | background-color: var(--sf-toolbar-gray-600); 274 | padding: 3px 6px; 275 | margin: 0 4px; 276 | min-width: 15px; 277 | min-height: 13px; 278 | text-align: center; 279 | } 280 | 281 | .sf-toolbar-block .sf-toolbar-status.sf-toolbar-status-green, 282 | .sf-toolbar-block .sf-toolbar-info .sf-toolbar-status.sf-toolbar-status-green { 283 | background-color: #059669; 284 | color: var(--white); 285 | } 286 | .sf-toolbar-block .sf-toolbar-status.sf-toolbar-status-red, 287 | .sf-toolbar-block .sf-toolbar-info .sf-toolbar-status.sf-toolbar-status-red { 288 | background-color: var(--sf-toolbar-red-500); 289 | color: var(--sf-toolbar-red-50); 290 | } 291 | .sf-toolbar-block .sf-toolbar-status.sf-toolbar-status-yellow, 292 | .sf-toolbar-block .sf-toolbar-info .sf-toolbar-status.sf-toolbar-status-yellow { 293 | background-color: var(--sf-toolbar-yellow-300); 294 | color: var(--sf-toolbar-yellow-800); 295 | } 296 | 297 | .sf-toolbar-block.sf-toolbar-status-green::before, 298 | .sf-toolbar-block.sf-toolbar-status-red::before, 299 | .sf-toolbar-block.sf-toolbar-status-yellow::before { 300 | background: var(--sf-toolbar-yellow-400); 301 | border-radius: 6px; 302 | content: ''; 303 | position: absolute; 304 | bottom: 1px; 305 | left: 0; 306 | width: 98%; 307 | height: 3px; 308 | z-index: 10005; 309 | } 310 | .sf-toolbar-block.sf-toolbar-status-red::before { 311 | background: var(--sf-toolbar-red-400); 312 | } 313 | .sf-toolbar-block.sf-toolbar-status-green::before { 314 | background: var(--sf-toolbar-green-400); 315 | } 316 | .sf-toolbar-block-request.sf-toolbar-block.sf-toolbar-status-green::before, 317 | .sf-toolbar-block-request.sf-toolbar-block.sf-toolbar-status-red::before, 318 | .sf-toolbar-block-request.sf-toolbar-block.sf-toolbar-status-yellow::before { 319 | display: none; 320 | } 321 | 322 | .sf-toolbar-block-request .sf-toolbar-status { 323 | border-radius: 6px; 324 | color: #fff; 325 | display: inline-block; 326 | flex-shrink: 0; 327 | font-size: 13px; 328 | font-weight: 500; 329 | padding: 4px 8px; 330 | } 331 | .sf-toolbar-block-request .sf-toolbar-info-piece a { 332 | background-color: transparent; 333 | text-decoration: none; 334 | } 335 | .sf-toolbar-block-request .sf-toolbar-info-piece a:hover { 336 | text-decoration: underline; 337 | } 338 | .sf-toolbar-block-request .sf-toolbar-redirection-status { 339 | font-weight: normal; 340 | padding: 2px 4px; 341 | line-height: 18px; 342 | } 343 | .sf-toolbar-block.sf-toolbar-block-request .sf-toolbar-redirection-status.sf-toolbar-status-yellow { 344 | background-color: var(--sf-toolbar-yellow-300); 345 | border-radius: 4px; 346 | color: var(--sf-toolbar-yellow-800); 347 | padding: 1px 4px; 348 | } 349 | .sf-toolbar-block.sf-toolbar-block-request .sf-toolbar-info-piece .sf-toolbar-redirection-method { 350 | background: transparent; 351 | color: var(--sf-toolbar-gray-300); 352 | border: 1px solid var(--sf-toolbar-gray-400); 353 | padding: 1px 4px; 354 | } 355 | .sf-toolbar-block-request .sf-toolbar-info-piece span.sf-toolbar-redirection-method { 356 | font-size: 12px; 357 | height: 17px; 358 | line-height: 17px; 359 | margin-right: 5px; 360 | } 361 | .sf-toolbar-block-request .sf-toolbar-request-icon svg { 362 | stroke-width: 3px; 363 | } 364 | 365 | .sf-toolbar-block-ajax .sf-toolbar-icon { 366 | cursor: pointer; 367 | } 368 | 369 | .sf-toolbar-status-green .sf-toolbar-label, 370 | .sf-toolbar-status-yellow .sf-toolbar-label, 371 | .sf-toolbar-status-red .sf-toolbar-label { 372 | color: var(--sf-toolbar-white); 373 | } 374 | .sf-toolbar-block-config svg path, 375 | .sf-toolbar-block-config svg .sf-svg-path { 376 | fill: var(--sf-toolbar-white); 377 | } 378 | 379 | .sf-toolbar-block .sf-toolbar-icon { 380 | color: var(--sf-toolbar-gray-300); 381 | align-items: center; 382 | display: flex; 383 | height: 36px; 384 | padding: 0 7px; 385 | overflow: hidden; 386 | text-overflow: ellipsis; 387 | } 388 | .sf-toolbar-block:hover .sf-toolbar-icon { 389 | border-bottom-left-radius: 4px; 390 | border-bottom-right-radius: 4px; 391 | box-shadow: 1px 0 0 var(--sf-toolbar-black), inset 0 -1px 0 var(--sf-toolbar-black); 392 | } 393 | .sf-toolbar-block.sf-toolbar-block-right:hover .sf-toolbar-icon { 394 | box-shadow: -1px 0 0 var(--sf-toolbar-black), inset 0 -1px 0 var(--sf-toolbar-black); 395 | } 396 | 397 | .sf-toolbar-block-request .sf-toolbar-icon { 398 | padding-left: 0; 399 | padding-right: 0; 400 | } 401 | 402 | .sf-toolbar-block .sf-toolbar-icon img, 403 | .sf-toolbar-block .sf-toolbar-icon svg { 404 | border-width: 0; 405 | } 406 | 407 | .sf-toolbar-block .sf-toolbar-icon img + span, 408 | .sf-toolbar-block .sf-toolbar-icon svg + span { 409 | margin-left: 4px; 410 | } 411 | .sf-toolbar-block-config .sf-toolbar-icon .sf-toolbar-value, 412 | .sf-toolbar-block.sf-toolbar-block-sf-cli .sf-toolbar-value { 413 | margin-left: 5px; 414 | } 415 | .sf-toolbar-block-config .sf-toolbar-icon .sf-toolbar-label, 416 | .sf-toolbar-block.sf-toolbar-block-sf-cli .sf-toolbar-label { 417 | margin-left: 0; 418 | } 419 | 420 | .sf-toolbar-block:hover, 421 | .sf-toolbar-block.hover { 422 | position: relative; 423 | } 424 | .sf-toolbar-block:hover .sf-toolbar-icon, 425 | .sf-toolbar-block.hover .sf-toolbar-icon { 426 | background-color: var(--sf-toolbar-gray-700); 427 | position: relative; 428 | z-index: 10002; 429 | } 430 | .sf-toolbar-block-ajax.hover .sf-toolbar-info { 431 | z-index: 10001; 432 | } 433 | .sf-toolbar-block:hover .sf-toolbar-info, 434 | .sf-toolbar-block.hover .sf-toolbar-info { 435 | display: block; 436 | padding: 10px; 437 | max-width: 525px; 438 | max-height: 480px; 439 | word-wrap: break-word; 440 | overflow: hidden; 441 | overflow-y: auto; 442 | } 443 | .sf-toolbar-info-piece b.sf-toolbar-ajax-info { 444 | color: var(--sf-toolbar-gray-100); 445 | } 446 | .sf-toolbar-ajax-requests { 447 | border: 1px solid var(--sf-toolbar-gray-500); 448 | font-variant: tabular-nums; 449 | margin: 5px 0 0; 450 | width: 100%; 451 | } 452 | .sf-toolbar-ajax-requests td { 453 | background-color: var(--sf-toolbar-gray-700); 454 | border: 1px solid var(--sf-toolbar-gray-500); 455 | color: var(--sf-toolbar-gray-100); 456 | font-size: 12px; 457 | padding: 4px; 458 | vertical-align: middle; 459 | } 460 | .sf-toolbar-ajax-requests thead { 461 | border: 0; 462 | } 463 | .sf-toolbar-ajax-requests th { 464 | background-color: var(--sf-toolbar-gray-800); 465 | border: 1px solid var(--sf-toolbar-gray-500); 466 | color: var(--sf-toolbar-gray-200); 467 | font-size: 11px; 468 | padding: 4px; 469 | } 470 | .sf-ajax-request-url { 471 | max-width: 250px; 472 | line-height: 9px; 473 | overflow: hidden; 474 | text-overflow: ellipsis; 475 | } 476 | .sf-toolbar-ajax-requests .sf-ajax-request-url a { 477 | text-decoration: none; 478 | } 479 | .sf-toolbar-ajax-requests .sf-ajax-request-url a:hover { 480 | text-decoration: underline; 481 | } 482 | .sf-ajax-request-duration { 483 | text-align: right; 484 | } 485 | .sf-toolbar-block .sf-toolbar-info-piece .sf-toolbar-ajax-requests .sf-toolbar-status { 486 | font-size: 11px; 487 | padding: 1px 3px; 488 | } 489 | .sf-ajax-request-loading { 490 | animation: sf-blink .5s ease-in-out infinite; 491 | } 492 | @keyframes sf-blink { 493 | 0% { background: var(--sf-toolbar-gray-800); } 494 | 50% { background: var(--sf-toolbar-gray-700); } 495 | 100% { background: var(--sf-toolbar-gray-800); } 496 | } 497 | 498 | .sf-toolbar-block.sf-toolbar-block-dump .sf-toolbar-info { 499 | max-width: none; 500 | width: 100%; 501 | position: fixed; 502 | box-sizing: border-box; 503 | left: 0; 504 | } 505 | 506 | .sf-toolbar-block-dump pre.sf-dump { 507 | background-color: var(--sf-toolbar-gray-800); 508 | border-color: var(--sf-toolbar-gray-500); 509 | border-radius: 0; 510 | margin: 6px 0 12px 0; 511 | } 512 | .sf-toolbar-block-dump pre.sf-dump:last-child { 513 | margin-bottom: 0; 514 | } 515 | .sf-toolbar-block-dump pre.sf-dump .sf-dump-search-wrapper { 516 | margin-bottom: 5px; 517 | } 518 | .sf-toolbar-block-dump pre.sf-dump span.sf-dump-search-count { 519 | color: #333; 520 | font-size: 12px; 521 | } 522 | .sf-toolbar-block-dump .sf-toolbar-info-piece { 523 | display: block; 524 | } 525 | .sf-toolbar-block-dump .sf-toolbar-info-piece .sf-toolbar-file-line { 526 | color: var(--sf-toolbar-gray-400); 527 | margin-left: 4px; 528 | } 529 | .sf-toolbar-block-dump .sf-toolbar-info img { 530 | display: none; 531 | } 532 | 533 | .sf-toolbar-block-serializer .detailed-metrics { 534 | display: grid; 535 | grid-template-columns: repeat(3, 1fr); 536 | grid-gap: 15px; 537 | margin-top: 15px; 538 | } 539 | 540 | /* Responsive Design */ 541 | .sf-toolbar-icon .sf-toolbar-label, 542 | .sf-toolbar-icon .sf-toolbar-value { 543 | display: none; 544 | } 545 | .sf-toolbar-block-config .sf-toolbar-icon .sf-toolbar-label, 546 | .sf-cli .sf-toolbar-icon .sf-toolbar-label { 547 | display: inline-block; 548 | } 549 | 550 | /* Legacy Design - these styles are maintained to make old panels look 551 | a bit better on the new toolbar */ 552 | .sf-toolbar-block .sf-toolbar-info-piece-additional-detail { 553 | color: var(--sf-toolbar-gray-400); 554 | font-size: 12px; 555 | } 556 | .sf-toolbar-status-green .sf-toolbar-info-piece-additional-detail, 557 | .sf-toolbar-status-yellow .sf-toolbar-info-piece-additional-detail, 558 | .sf-toolbar-status-red .sf-toolbar-info-piece-additional-detail { 559 | color: var(--sf-toolbar-white); 560 | } 561 | 562 | @media (min-width: 768px) { 563 | .sf-toolbar-icon .sf-toolbar-label, 564 | .sf-toolbar-icon .sf-toolbar-value { 565 | display: inline; 566 | } 567 | 568 | .sf-toolbar-block-time .sf-toolbar-icon svg, 569 | .sf-toolbar-block-memory .sf-toolbar-icon svg { 570 | display: none; 571 | } 572 | .sf-toolbar-block-time .sf-toolbar-icon svg + span, 573 | .sf-toolbar-block-memory .sf-toolbar-icon svg + span { 574 | margin-left: 0; 575 | } 576 | 577 | .sf-toolbar-block .sf-toolbar-icon { 578 | padding: 0 10px; 579 | } 580 | .sf-toolbar-block-time .sf-toolbar-icon { 581 | padding-right: 5px; 582 | } 583 | .sf-toolbar-block-memory .sf-toolbar-icon { 584 | padding-left: 5px; 585 | } 586 | .sf-toolbar-block-request .sf-toolbar-icon { 587 | display: flex; 588 | align-items: center; 589 | padding-left: 0; 590 | padding-right: 0; 591 | } 592 | .sf-toolbar-block-request .sf-toolbar-label { 593 | margin-left: 4px; 594 | margin-right: 1px; 595 | } 596 | .sf-toolbar-block-request .sf-toolbar-status + .sf-toolbar-request-icon { 597 | display: inline-flex; 598 | margin-left: 5px; 599 | } 600 | .sf-toolbar-block-request .sf-toolbar-icon .sf-toolbar-request-icon + .sf-toolbar-label { 601 | margin-left: 0; 602 | } 603 | .sf-toolbar-block-request .sf-toolbar-label + .sf-toolbar-value { 604 | margin-right: 5px; 605 | } 606 | 607 | .sf-toolbar-block-request:hover .sf-toolbar-info { 608 | max-width: none; 609 | } 610 | 611 | .sf-toolbar-block .sf-toolbar-info-piece b { 612 | font-size: 12px; 613 | } 614 | .sf-toolbar-block .sf-toolbar-info-piece span { 615 | font-size: 13px; 616 | } 617 | 618 | .sf-toolbar-block-right { 619 | float: right; 620 | margin-left: 0; 621 | margin-right: 0; 622 | } 623 | .sf-toolbarreset .sf-toolbar-block.sf-toolbar-block-right:not(.sf-toolbar-block-sf-cli) .sf-toolbar-info { 624 | border-bottom-left-radius: 4px; 625 | border-bottom-right-radius: 0; 626 | } 627 | } 628 | 629 | @media (min-width: 1024px) { 630 | .sf-toolbar-block .sf-toolbar-info-piece-additional, 631 | .sf-toolbar-block .sf-toolbar-info-piece-additional-detail { 632 | display: inline; 633 | } 634 | 635 | .sf-toolbar-block .sf-toolbar-info-piece-additional:empty, 636 | .sf-toolbar-block .sf-toolbar-info-piece-additional-detail:empty { 637 | display: none; 638 | } 639 | } 640 | 641 | /***** Error Toolbar *****/ 642 | .sf-error-toolbar .sf-toolbarreset { 643 | background: var(--sf-toolbar-gray-800); 644 | color: var(--sf-toolbar-gray-100); 645 | font: 13px/36px var(--sf-toolbar-font-family-system); 646 | height: 36px; 647 | padding: 0 15px; 648 | text-align: left; 649 | } 650 | 651 | .sf-error-toolbar .sf-toolbarreset svg { 652 | height: auto; 653 | } 654 | 655 | .sf-error-toolbar .sf-toolbarreset a { 656 | color: #99cdd8; 657 | margin-left: 5px; 658 | text-decoration: underline; 659 | } 660 | 661 | .sf-error-toolbar .sf-toolbarreset a:hover { 662 | text-decoration: none; 663 | } 664 | 665 | .sf-error-toolbar .sf-toolbarreset .sf-toolbar-icon { 666 | float: left; 667 | padding: 5px 0; 668 | margin-right: 10px; 669 | } 670 | 671 | .sf-full-stack { 672 | left: 0px; 673 | font-size: 12px; 674 | } 675 | 676 | /***** Media query print: Do not print the Toolbar. *****/ 677 | @media print { 678 | .sf-toolbar { 679 | display: none !important; 680 | } 681 | } 682 | -------------------------------------------------------------------------------- /resources/css/custom.css: -------------------------------------------------------------------------------- 1 | .Whoops.container { 2 | z-index: 99998; 3 | } 4 | 5 | .sf-toolbar-block.sf-toolbar-block-fullwidth .sf-toolbar-info { 6 | max-width: none; 7 | width: 100%; 8 | position: fixed; 9 | box-sizing: border-box; 10 | left: 0; 11 | } 12 | 13 | .sf-toolbar-previews { 14 | table-layout: auto; 15 | width: 100%; 16 | } 17 | 18 | .sf-toolbar-previews td { 19 | background-color: #444; 20 | border-bottom: 1px solid #777; 21 | color: #F5F5F5; 22 | font-size: 12px; 23 | padding: 4px; 24 | } 25 | 26 | .sf-toolbar-previews td.monospace { 27 | font-family: SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace; 28 | font-size: 10px; 29 | } 30 | 31 | .sf-toolbar-previews td.sf-query { 32 | font-family: SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace; 33 | font-size: 11px; 34 | white-space: normal; 35 | vertical-align: middle; 36 | } 37 | 38 | .sf-toolbar-previews tr:last-child td { 39 | border-bottom: 0; 40 | } 41 | 42 | .sf-toolbar-previews th { 43 | background-color: #222; 44 | border-bottom: 0; 45 | color: #AAA; 46 | font-size: 11px; 47 | padding: 4px; 48 | } 49 | 50 | .sf-minitoolbar .open-button svg { 51 | max-height: 18px; 52 | margin-top: 4px; 53 | } 54 | .sf-toolbar-block-config svg path, 55 | .sf-toolbar-block-config svg .sf-svg-path { 56 | fill: #FF2929; 57 | } 58 | -------------------------------------------------------------------------------- /resources/css/theme_light.css: -------------------------------------------------------------------------------- 1 | 2 | .sf-toolbarreset { 3 | background-color: #EEF1F3; 4 | box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.2); 5 | color: #212529; 6 | } 7 | 8 | .sf-toolbarreset .hide-button, .sf-toolbarreset .hide-button:hover { 9 | background: #EEF1F3; 10 | color: var(--sf-toolbar-gray-800); 11 | } 12 | 13 | .sf-toolbar-block .sf-toolbar-value { 14 | color: #212529; 15 | } 16 | 17 | .sf-toolbar-block .sf-toolbar-label, 18 | .sf-toolbar-block .sf-toolbar-class-separator { 19 | color: #212529; 20 | } 21 | 22 | .sf-toolbar-block hr { 23 | border-top: 1px solid #212529; 24 | } 25 | 26 | .sf-toolbar-block .sf-toolbar-info-group { 27 | border-bottom: 1px solid #212529; 28 | } 29 | 30 | div.sf-toolbar .sf-toolbar-block .sf-toolbar-info-piece a { 31 | color: #3439bc; 32 | } 33 | 34 | .sf-toolbar-block .sf-toolbar-info-piece b { 35 | color: #212529; 36 | } 37 | 38 | .sf-toolbar-block:not(.sf-toolbar-block-dump) .sf-toolbar-info-piece span { 39 | color: #212529; 40 | } 41 | 42 | .sf-toolbar-block:not(.sf-toolbar-block-dump) .sf-toolbar-info-piece span.sf-toolbar-status { 43 | color: #FFF; 44 | } 45 | 46 | .sf-toolbar-block .sf-toolbar-info { 47 | background-color: #fff; 48 | color: #212529; 49 | } 50 | 51 | .sf-toolbar-block .sf-toolbar-status { 52 | background-color: #212529; 53 | } 54 | 55 | 56 | .sf-toolbar-block .sf-toolbar-icon { 57 | color: var(--sf-toolbar-gray-800); 58 | } 59 | 60 | .sf-toolbar-block:hover .sf-toolbar-icon, 61 | .sf-toolbar-block.hover .sf-toolbar-icon { 62 | background-color: #fff; 63 | border-bottom: 2px solid #4040c8; 64 | box-sizing: border-box; 65 | } 66 | 67 | .sf-toolbar-block:hover .sf-toolbar-info, 68 | .sf-toolbar-block.hover .sf-toolbar-info { 69 | box-shadow: 0px -2px 6px rgba(0,0,0,0.1); 70 | border-top-left-radius: 3px; 71 | border-top-right-radius: 3px; 72 | } 73 | 74 | .sf-toolbar-info-piece b.sf-toolbar-ajax-info { 75 | color: #212529; 76 | } 77 | 78 | .sf-toolbar-ajax-requests td, .sf-toolbar-previews td { 79 | background-color: #fff; 80 | border-bottom: 1px solid #212529; 81 | color: #212529; 82 | } 83 | 84 | .sf-toolbar-ajax-requests th, .sf-toolbar-previews th { 85 | background-color: #EEF1F3; 86 | color: #212529; 87 | } 88 | 89 | @keyframes sf-blink { 90 | 0% { background: #EEF1F3; } 91 | 50% { background: #444; } 92 | 100% { background: #EEF1F3; } 93 | } 94 | 95 | .sf-toolbar-block .sf-toolbar-info-piece-additional-detail { 96 | color: #EEF1F3; 97 | } 98 | 99 | .sf-error-toolbar .sf-toolbarreset { 100 | background: #EEF1F3; 101 | color: #212529; 102 | } 103 | 104 | .sf-error-toolbar .sf-toolbarreset a { 105 | color: #3439bc; 106 | } 107 | -------------------------------------------------------------------------------- /resources/icons/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Icons are from "Tabler Icons" (https://github.com/tabler/tabler-icons), a set of 2 | free MIT-licensed high-quality SVG icons. 3 | 4 | ----- 5 | 6 | MIT License 7 | 8 | Copyright (c) 2020-2022 Paweł Kuna 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | -------------------------------------------------------------------------------- /resources/icons/ajax.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /resources/icons/cache.svg: -------------------------------------------------------------------------------- 1 | 2 | Cache 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /resources/icons/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/icons/commands.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/icons/config.svg: -------------------------------------------------------------------------------- 1 | 2 | Config 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /resources/icons/dumps.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/icons/events.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /resources/icons/exceptions.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/icons/forward.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/icons/gates.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/icons/http-client.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/icons/jobs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /resources/icons/laravel.svg: -------------------------------------------------------------------------------- 1 | 2 | Logomark 3 | -------------------------------------------------------------------------------- /resources/icons/logs.svg: -------------------------------------------------------------------------------- 1 | 2 | Logger 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /resources/icons/mail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/icons/memory.svg: -------------------------------------------------------------------------------- 1 | 2 | Memory 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /resources/icons/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | Menu 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /resources/icons/models.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/icons/notifications.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/icons/queries.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/icons/redirect.svg: -------------------------------------------------------------------------------- 1 | 2 | Redirect 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/icons/redis.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/icons/refresh.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/icons/requests.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/icons/schedule.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/icons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/icons/session.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/icons/telescope.svg: -------------------------------------------------------------------------------- 1 | 2 | telescope 3 | -------------------------------------------------------------------------------- /resources/icons/time.svg: -------------------------------------------------------------------------------- 1 | 2 | Time 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /resources/icons/user.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/icons/validator.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/icons/views.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/views/base_js.blade.php: -------------------------------------------------------------------------------- 1 | 677 | 678 | 679 | -------------------------------------------------------------------------------- /resources/views/collectors/ajax.blade.php: -------------------------------------------------------------------------------- 1 | @component('telescope-toolbar::item', ['name' => 'ajax', 'additional_classes' => 'sf-toolbar-block-right']) 2 | 3 | @slot('icon') 4 | 5 | @ttIcon('ajax') 6 | 7 | 0 8 | 9 | @endslot 10 | 11 | @slot("text") 12 |
13 | 14 | 15 | (Clear) 16 | 17 |
18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
#ProfileMethodTypeStatusURLTime
33 |
34 | 35 | @endslot 36 | @endcomponent 37 | -------------------------------------------------------------------------------- /resources/views/collectors/cache.blade.php: -------------------------------------------------------------------------------- 1 | content['type']])) { 13 | $types[$entry->content['type']] = 0; 14 | } 15 | $types[$entry->content['type']]++; 16 | } 17 | 18 | ?> 19 | @component('telescope-toolbar::item', ['name' => 'queries', 'link' => true]) 20 | 21 | @slot('icon') 22 | @ttIcon('cache') 23 | 24 | {{ $calls }} 25 | 26 | @if (isset($types['missed'])) 27 | 28 | ({{ $types['missed'] }} miss) 29 | 30 | @endif 31 | @endslot 32 | 33 | @slot('text') 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | @foreach ($entries as $entry) 44 | 45 | 48 | 51 | 52 | @endforeach 53 | 54 |
KeyAction
46 | {{ \Illuminate\Support\Str::limit($entry->content['key'], 60) }} 47 | 49 | {{ $entry->content['type'] }} 50 |
55 | 56 | @endslot 57 | 58 | @endcomponent -------------------------------------------------------------------------------- /resources/views/collectors/commands.blade.php: -------------------------------------------------------------------------------- 1 | 8 | @component('telescope-toolbar::item', ['name' => 'commands', 'link' => true]) 9 | 10 | @slot('icon') 11 | @ttIcon('commands') 12 | 13 | {{ $entries->count() }} 14 | 15 | @endslot 16 | 17 | 18 | @endcomponent -------------------------------------------------------------------------------- /resources/views/collectors/config.blade.php: -------------------------------------------------------------------------------- 1 | @component('telescope-toolbar::item', ['name' => 'config', 'additional_classes' => 'sf-toolbar-block-right']) 2 | 3 | @slot('icon') 4 | 5 | @ttIcon('laravel') 6 | 7 | {{ app()->version() }} 8 | 9 | @endslot 10 | 11 | @slot("text") 12 |
13 |
14 | Environment 15 | {{ app()->environment() }} 16 |
17 | 18 |
19 | Debug 20 | {{ config('app.debug') ? 'enabled' : 'disabled' }} 21 |
22 | 23 |
24 |
25 | PHP version 26 | {{ phpversion() }} 27 |
28 |
29 | Laravel version 30 | {{ app()->version() }} 31 |
32 |
33 |
34 | 35 | @endslot 36 | @endcomponent 37 | -------------------------------------------------------------------------------- /resources/views/collectors/dumps.blade.php: -------------------------------------------------------------------------------- 1 | 8 | @component('telescope-toolbar::item', ['name' => 'dump', 'link' => route('telescope') . '/dumps', 'status' => 'yellow']) 9 | 10 | @slot('icon') 11 | @ttIcon('dumps') 12 | 13 | {{ $entries->count() }} 14 | 15 | @endslot 16 | 17 | @slot("text") 18 | 19 | @foreach ($entries as $entry) 20 |
21 |
22 | {!! $entry->content['dump'] !!} 23 |
24 | 25 |
26 | 27 | @endforeach 28 | 29 | @endslot 30 | 31 | @endcomponent -------------------------------------------------------------------------------- /resources/views/collectors/events.blade.php: -------------------------------------------------------------------------------- 1 | 8 | @component('telescope-toolbar::item', ['name' => 'events', 'link' => '#events']) 9 | 10 | @slot('icon') 11 | @ttIcon('events') 12 | 13 | {{ $entries->count() }} 14 | 15 | @endslot 16 | 17 | 18 | @endcomponent 19 | -------------------------------------------------------------------------------- /resources/views/collectors/exceptions.blade.php: -------------------------------------------------------------------------------- 1 | 8 | @component('telescope-toolbar::item', ['name' => 'exceptions', 'link' => true, 'status' => 'red']) 9 | 10 | @slot('icon') 11 | @ttIcon('exceptions') 12 | 13 | {{ $entries->count() }} 14 | 15 | @endslot 16 | 17 | @slot('text') 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | @foreach ($entries as $entry) 30 | 31 | 36 | 37 | 41 | 42 | 43 | @endforeach 44 | 45 | 46 |
DetailsMessage
32 | 33 | view 34 | 35 | 38 | {{ \Illuminate\Support\Str::limit($entry->content['class'], 70) }}
39 | {{ \Illuminate\Support\Str::limit($entry->content['message'], 100) }} 40 |
47 |
48 | @endslot 49 | 50 | @endcomponent -------------------------------------------------------------------------------- /resources/views/collectors/gates.blade.php: -------------------------------------------------------------------------------- 1 | 8 | @component('telescope-toolbar::item', ['name' => 'gates', 'link' => true]) 9 | 10 | @slot('icon') 11 | @ttIcon('gates') 12 | 13 | {{ $entries->count() }} 14 | 15 | @endslot 16 | 17 | 18 | @slot('text') 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | @foreach ($entries as $entry) 29 | 30 | 33 | 36 | 37 | @endforeach 38 | 39 |
AbilityResult
31 | {{ \Illuminate\Support\Str::limit($entry->content['ability'], 60) }} 32 | 34 | {{ $entry->content['result'] }} 35 |
40 | 41 | @endslot 42 | 43 | @endcomponent -------------------------------------------------------------------------------- /resources/views/collectors/jobs.blade.php: -------------------------------------------------------------------------------- 1 | 8 | @component('telescope-toolbar::item', ['name' => 'jobs', 'link' => true]) 9 | 10 | @slot('icon') 11 | @ttIcon('jobs') 12 | 13 | {{ $entries->count() }} 14 | 15 | @endslot 16 | 17 | @slot('text') 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | @foreach ($entries as $entry) 30 | 31 | 36 | 37 | 43 | 44 | @endforeach 45 | 46 | 47 | 48 |
DetailsMessage
32 | 33 | view 34 | 35 | 38 | {{ \Illuminate\Support\Str::limit($entry->content['name'], 70) }}
39 | 40 | Connection: {{ $entry->content['connection'] }} | Queue: {{ $entry->content['queue'] }} 41 | 42 |
49 |
50 | @endslot 51 | @endcomponent -------------------------------------------------------------------------------- /resources/views/collectors/logs.blade.php: -------------------------------------------------------------------------------- 1 | content['level']; 14 | 15 | if (in_array($level, ['debug', 'info'])) { 16 | $info++; 17 | } elseif (in_array($level, ['notice', 'warning'])) { 18 | $warnings++; 19 | } else { 20 | $errors++; 21 | } 22 | 23 | if (!isset($levels[$entry->content['level']])) { 24 | $levels[$entry->content['level']] = 0; 25 | } 26 | $levels[$entry->content['level']]++; 27 | 28 | $total++; 29 | } 30 | 31 | if ($errors) { 32 | $statusColor = 'red'; 33 | } elseif ($warnings) { 34 | $statusColor = 'yellow'; 35 | } else { 36 | $statusColor = null; 37 | } 38 | 39 | ?> 40 | @component('telescope-toolbar::item', ['name' => 'logs ', 'link' => true, 'status' => $statusColor]) 41 | 42 | @slot('icon') 43 | @ttIcon('logs') 44 | 45 | {{ $total }} 46 | 47 | @endslot 48 | 49 | @slot('text') 50 | 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | @foreach ($entries as $entry) 63 | 64 | 69 | 70 | 73 | 74 | 77 | 78 | 79 | @endforeach 80 | 81 | 82 |
DetailsLevelMessage
65 | 66 | view 67 | 68 | 71 | {{ $entry->content['level'] }} 72 | 75 | {{ \Illuminate\Support\Str::limit($entry->content['message'], 90) }} 76 |
83 |
84 | @endslot 85 | 86 | 87 | @endcomponent -------------------------------------------------------------------------------- /resources/views/collectors/mail.blade.php: -------------------------------------------------------------------------------- 1 | 8 | @component('telescope-toolbar::item', ['name' => 'mail', 'link' => true]) 9 | 10 | @slot('icon') 11 | @ttIcon('mail') 12 | 13 | {{ $entries->count() }} 14 | 15 | @endslot 16 | 17 | @slot('text') 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | @foreach ($entries as $entry) 30 | 31 | 36 | 37 | 46 | 47 | @endforeach 48 | 49 | 50 | 51 |
DetailsMessage
32 | 33 | view 34 | 35 | 38 | {{ \Illuminate\Support\Str::limit($entry->content['mailable'] ?: '-', 70) }}
39 | 40 | @if($entry->content['queued']) 41 | Queued | 42 | @endif 43 | Subject: {{ \Illuminate\Support\Str::limit($entry->content['subject'], 90) }} 44 | 45 |
52 |
53 | 54 | @endslot 55 | 56 | @endcomponent -------------------------------------------------------------------------------- /resources/views/collectors/memory.blade.php: -------------------------------------------------------------------------------- 1 | first()->content; 4 | 5 | $memory = $data['memory'] ?? null; 6 | if (!$memory) { 7 | return; 8 | } 9 | 10 | $statusColor = null; 11 | if ($memory > 50) { 12 | $statusColor = 'yellow'; 13 | } elseif ($memory > 10) { 14 | $memory = round($memory); 15 | } 16 | ?> 17 | 18 | @component('telescope-toolbar::item', ['name' => 'memory', 'link' => true]) 19 | 20 | @slot('icon') 21 | 22 | @ttIcon('memory') 23 | 24 | {{ $memory }} 25 | MB 26 | @endslot 27 | 28 | @slot('text') 29 | 30 |
31 | Peak memory usage 32 | {{ $data['memory'] }} MB 33 |
34 | 35 | @endslot 36 | 37 | 38 | @endcomponent -------------------------------------------------------------------------------- /resources/views/collectors/models.blade.php: -------------------------------------------------------------------------------- 1 | 8 | @component('telescope-toolbar::item', ['name' => 'models', 'link' => true]) 9 | 10 | @slot('icon') 11 | @ttIcon('models') 12 | 13 | {{ $entries->count() }} 14 | 15 | @endslot 16 | 17 | @slot('text') 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | @foreach ($entries as $entry) 31 | 32 | 35 | 36 | 41 | 42 | @endforeach 43 | 44 | 45 |
ActionModel
33 | {{ $entry->content['action'] }} 34 | 37 | 38 | {{ $entry->content['model'] }} 39 | 40 |
46 |
47 | 48 | @endslot 49 | 50 | @endcomponent -------------------------------------------------------------------------------- /resources/views/collectors/notifications.blade.php: -------------------------------------------------------------------------------- 1 | 8 | @component('telescope-toolbar::item', ['name' => 'notifications', 'link' => true]) 9 | 10 | @slot('icon') 11 | @ttIcon('notifications') 12 | 13 | {{ $entries->count() }} 14 | 15 | @endslot 16 | 17 | @slot('text') 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | @foreach ($entries as $entry) 31 | 32 | 37 | 38 | 46 | 47 | 53 | 54 | 55 | @endforeach 56 | 57 | 58 | 59 |
DetailsChannelMessage
33 | 34 | view 35 | 36 | 39 | {{ \Illuminate\Support\Str::limit($entry->content['channel'] ?: '-', 20) }} 40 | @if($entry->content['queued']) 41 |
queued 42 | @else 43 |
sync 44 | @endif 45 |
48 | {{ \Illuminate\Support\Str::limit($entry->content['notification'] ?: '-', 70) }}
49 | 50 | Recipient: {{ \Illuminate\Support\Str::limit($entry->content['notifiable'], 90) }} 51 | 52 |
60 |
61 | 62 | @endslot 63 | 64 | @endcomponent -------------------------------------------------------------------------------- /resources/views/collectors/queries.blade.php: -------------------------------------------------------------------------------- 1 | content['slow'] ?? false) { 13 | $num_slow++; 14 | } 15 | $query_time += (float) str_replace(',', '', $query->content['time']) ?? 0; 16 | $queries[$query->content['hash'] ?? $query->content['sql']] = $query->content['sql']; 17 | } 18 | 19 | $num_duplicated = $num_queries - count($queries); 20 | if ($num_queries > 0 && $num_duplicated > $num_queries *.75) { 21 | $statusColor = 'yellow'; 22 | } else { 23 | $statusColor = null; 24 | } 25 | ?> 26 | @component('telescope-toolbar::item', ['name' => 'queries', 'link' => '#queries', 'status' => $statusColor, 'additional_classes' => 'sf-toolbar-block-fullwidth']) 27 | 28 | @slot('icon') 29 | @ttIcon('queries') 30 | 31 | {{ $num_queries }} 32 | 33 | 34 | in 35 | {{ round($query_time) }} 36 | ms 37 | 38 | 39 | @endslot 40 | 41 | @slot('text') 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | @foreach ($entries as $query) 53 | @php($path = str_replace(base_path(), '', $query->content['file'])) 54 | 55 | 58 | 59 | 63 | 64 | @endforeach 65 | 66 | 67 |
Query
{{ $num_queries }} queries, {{ $num_duplicated }} of which are duplicated and {{ $num_slow }} slow.
Duration
{{ number_format($query_time, 2) }} ms
56 | {{ $query->content['sql'] }} 57 | 60 | {{ number_format((float) str_replace(',', '', $query->content['time']), 2) }}ms
61 | {{ strlen($path) > 32 ? '..' . substr($path, -30) : $path }}:{{ $query->content['line'] }} 62 |
68 | 69 | @endslot 70 | 71 | @endcomponent 72 | -------------------------------------------------------------------------------- /resources/views/collectors/redis.blade.php: -------------------------------------------------------------------------------- 1 | 8 | @component('telescope-toolbar::item', ['name' => 'redis', 'link' => true]) 9 | 10 | @slot('icon') 11 | @ttIcon('redis') 12 | 13 | {{ $entries->count() }} 14 | 15 | @endslot 16 | 17 | 18 | @endcomponent -------------------------------------------------------------------------------- /resources/views/collectors/request.blade.php: -------------------------------------------------------------------------------- 1 | first()->content; 4 | 5 | $statusCode = $data['response_status']; 6 | if ($statusCode > 400) { 7 | $statusColor = 'red'; 8 | } elseif ($statusCode > 300) { 9 | $statusColor = 'yellow'; 10 | } else { 11 | $statusColor = 'green'; 12 | } 13 | ?> 14 | 15 | @component('telescope-toolbar::item', ['name' => 'request', 'link' => true]) 16 | 17 | @slot('icon') 18 | {{ $statusCode }} 19 | {{ $data['method'] }} {{ $data['uri'] }} 20 | @endslot 21 | 22 | @slot('text') 23 |
24 |
25 | HTTP status 26 | {{ $statusCode }} 27 |
28 | 29 | @if($data['method'] !== 'GET') 30 |
31 | Method 32 | {{ $data['method'] }} 33 |
34 | @endif 35 | 36 |
37 | Request URI 38 | {{ $data['method'] }} {{ $data['uri'] }} 39 |
40 | 41 |
42 | Controller Action 43 | 44 | {{ $data['controller_action'] }} 45 | 46 |
47 | 48 |
49 | Middleware 50 | 51 | {{ implode(', ', array_filter($data['middleware'])) ?: '-' }} 52 | 53 |
54 | 55 | @if(isset($data['response']['view'])) 56 |
57 | View 58 | 59 | {{ str_replace(base_path(), '', $data['response']['view']) }} 60 | 61 |
62 | @elseif(isset($data['response']) && is_string($data['response'])) 63 |
64 | Response 65 | 66 | {{ \Illuminate\Support\Str::limit($data['response'], 60) }} 67 | 68 |
69 | @endif 70 | 71 |
72 | @endslot 73 | 74 | @endcomponent 75 | -------------------------------------------------------------------------------- /resources/views/collectors/session.blade.php: -------------------------------------------------------------------------------- 1 | first()->content; 4 | 5 | $dumper = new \Symfony\Component\VarDumper\Dumper\HtmlDumper(); 6 | $varCloner = new \Symfony\Component\VarDumper\Cloner\VarCloner(); 7 | 8 | ?> 9 | 10 | @component('telescope-toolbar::item', ['name' => 'dump', 'link' => false]) 11 | 12 | @slot('icon') 13 | @ttIcon('session') 14 | Session 15 | @endslot 16 | 17 | @slot('text') 18 | 19 | @if(isset($data['session'])) 20 |
21 |
22 | 23 | {!! $dumper->dump($varCloner->cloneVar($data['session'])) !!} 24 | 25 |
26 |
27 | @endif 28 | 29 | 30 | @endslot 31 | 32 | @endcomponent 33 | -------------------------------------------------------------------------------- /resources/views/collectors/time.blade.php: -------------------------------------------------------------------------------- 1 | first()->content; 4 | 5 | ?> 6 | 7 | @component('telescope-toolbar::item', ['name' => 'time', 'link' => true]) 8 | 9 | @slot('icon') 10 | 11 | @ttIcon('time') 12 | 13 | {{ $data['duration'] }} 14 | ms 15 | @endslot 16 | 17 | @slot('text') 18 | 19 |
20 | Request Duration 21 | {{ $data['duration'] }} ms 22 |
23 | 24 |
25 | Peak memory usage 26 | {{ $data['memory'] }} MB 27 |
28 | 29 | @endslot 30 | 31 | @endcomponent 32 | -------------------------------------------------------------------------------- /resources/views/collectors/user.blade.php: -------------------------------------------------------------------------------- 1 | first()->content['user'] ?? []; 4 | 5 | ?> 6 | 7 | @component('telescope-toolbar::item', ['name' => 'user', 'link' => true]) 8 | 9 | @slot('icon') 10 | 11 | @ttIcon('user') 12 | 13 | {{ $data['email'] ?? 'n/a' }} 14 | @endslot 15 | 16 | @slot('text') 17 | @if($data) 18 |
19 | @foreach ($data as $key => $value) 20 | @if (!empty($value)) 21 |
22 | {{ $key }} 23 | {{ $value }} 24 |
25 | @endif 26 | @endforeach 27 |
28 | @endif 29 | @endslot 30 | 31 | @endcomponent -------------------------------------------------------------------------------- /resources/views/collectors/views.blade.php: -------------------------------------------------------------------------------- 1 | content['name']; 10 | 11 | if (isset($views[$name])) { 12 | $views[$name]['num']++; 13 | } 14 | 15 | $views[$name] = [ 16 | 'id' => $entry->id, 17 | 'name' => $name, 18 | 'path' => $entry->content['path'] ?? '', 19 | 'num' => 1, 20 | ]; 21 | $total++; 22 | } 23 | 24 | $views = collect($views)->reverse(); 25 | 26 | ?> 27 | @component('telescope-toolbar::item', ['name' => 'views', 'link' => '#views']) 28 | 29 | @slot('icon') 30 | @ttIcon('views') 31 | 32 | {{ $total }} 33 | 34 | @endslot 35 | 36 | @slot('text') 37 | 38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | @foreach ($views as $view) 50 | 51 | 56 | 57 | 60 | 61 | 64 | 65 | 66 | @endforeach 67 | 68 | 69 |
NamePathNum
52 | 53 | {{ \Illuminate\Support\Str::limit($view['name'], 40) }} 54 | 55 | 58 | {{ \Illuminate\Support\Str::limit($view['path'], 40) }} 59 | 62 | {{ $view['num'] }} 63 |
70 |
71 | @endslot 72 | 73 | 74 | @endcomponent 75 | -------------------------------------------------------------------------------- /resources/views/head.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | @if ($lightMode) 6 | 7 | @endif 8 | 9 | 16 | 17 | -------------------------------------------------------------------------------- /resources/views/item.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @if(isset($link) && $link) 3 | @php 4 | $ttLink = route('telescope-toolbar.show', ['token' => $token, 'tab' => $name]); 5 | if ($link === true) { 6 | $link = $ttLink; 7 | } elseif (\Illuminate\Support\Str::startsWith($link, '#')) { 8 | $link = $ttLink . $link; 9 | } 10 | @endphp 11 | 12 | @endif 13 |
{{ $icon ?? '' }}
14 | @if(isset($link) && $link)
@endif 15 |
{{ $text ?? '' }}
16 |
17 | -------------------------------------------------------------------------------- /resources/views/toolbar.blade.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 |
7 | 10 |
11 |
12 | 13 |
14 | 15 | @foreach (config('telescope-toolbar.collectors') as $type => $templates) 16 | @if(isset($entries[$type])) 17 | @foreach($templates as $template) 18 | @include($template, ['entries' => $entries[$type]]) 19 | @endforeach 20 | @endif 21 | @endforeach 22 | 23 | @include("telescope-toolbar::collectors.config") 24 | @include("telescope-toolbar::collectors.ajax") 25 | 26 | 29 |
30 | 31 | -------------------------------------------------------------------------------- /resources/views/widget.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /src/Http/Controllers/ToolbarController.php: -------------------------------------------------------------------------------- 1 | entriesRepository = $entriesRepository; 24 | 25 | $this->middleware(function($request, $next) { 26 | Telescope::stopRecording(); 27 | 28 | if ($request->hasSession()) { 29 | $request->session()->reflash(); 30 | } 31 | 32 | return $next($request); 33 | }); 34 | } 35 | 36 | public function render($token) 37 | { 38 | $this->prepareBlade($token); 39 | 40 | $options = $this->findBatchOptions($token); 41 | 42 | $entries = $this->entriesRepository->get(null, $options)->groupBy('type'); 43 | 44 | return View::make('telescope-toolbar::toolbar', [ 45 | 'entries' => $entries, 46 | ]); 47 | } 48 | 49 | public function show($token) 50 | { 51 | $options = $this->findBatchOptions($token); 52 | 53 | $request = $this->entriesRepository->get('request', $options)->first(); 54 | 55 | return redirect(route('telescope') . '/requests/' . $request->id); 56 | } 57 | 58 | public function baseJs() 59 | { 60 | $content = View::make('telescope-toolbar::base_js', [ 61 | 'excluded_ajax_paths' => config('telescope-toolbar.excluded_ajax_paths', '^/_tt'), 62 | ])->render(); 63 | 64 | $content = $this->stripSurroundingTags($content); 65 | 66 | return response($content, 200, [ 67 | 'Content-Type' => 'text/javascript', 68 | ])->setClientTtl(31536000); 69 | } 70 | 71 | public function styling(Request $request) 72 | { 73 | if ($request->get('lightMode')) { 74 | $files = ['theme_light.css']; 75 | } else { 76 | $files = [ 77 | 'base.css', 78 | 'custom.css', 79 | ]; 80 | } 81 | 82 | $content = ''; 83 | foreach ($files as $file) { 84 | $content .= File::get(__DIR__ . '/../../../resources/css/' . $file) . "\n"; 85 | } 86 | 87 | return response($content, 200, [ 88 | 'Content-Type' => 'text/css', 89 | ])->setClientTtl(31536000); 90 | } 91 | 92 | /** 93 | * Strip