├── .gitattributes
├── tests
├── resources
│ └── views
│ │ └── dashboard.blade.php
├── ToolbarTest.php
├── BrowserTestCase.php
├── TestCase.php
├── ToolbarBrowserTest.php
└── TelescopeFeatureTestCase.php
├── .gitignore
├── src
├── helpers.php
├── Http
│ ├── assets.php
│ ├── routes.php
│ └── Controllers
│ │ └── ToolbarController.php
├── ToolbarServiceProvider.php
└── Toolbar.php
├── resources
├── icons
│ ├── dumps.svg
│ ├── session.svg
│ ├── user.svg
│ ├── refresh.svg
│ ├── close.svg
│ ├── search.svg
│ ├── validator.svg
│ ├── mail.svg
│ ├── queries.svg
│ ├── redirect.svg
│ ├── models.svg
│ ├── commands.svg
│ ├── requests.svg
│ ├── forward.svg
│ ├── menu.svg
│ ├── ajax.svg
│ ├── cache.svg
│ ├── schedule.svg
│ ├── notifications.svg
│ ├── views.svg
│ ├── gates.svg
│ ├── exceptions.svg
│ ├── jobs.svg
│ ├── events.svg
│ ├── logs.svg
│ ├── time.svg
│ ├── http-client.svg
│ ├── telescope.svg
│ ├── memory.svg
│ ├── config.svg
│ ├── redis.svg
│ ├── LICENSE.txt
│ └── laravel.svg
├── views
│ ├── collectors
│ │ ├── redis.blade.php
│ │ ├── commands.blade.php
│ │ ├── events.blade.php
│ │ ├── dumps.blade.php
│ │ ├── time.blade.php
│ │ ├── session.blade.php
│ │ ├── memory.blade.php
│ │ ├── user.blade.php
│ │ ├── gates.blade.php
│ │ ├── ajax.blade.php
│ │ ├── config.blade.php
│ │ ├── models.blade.php
│ │ ├── jobs.blade.php
│ │ ├── exceptions.blade.php
│ │ ├── mail.blade.php
│ │ ├── cache.blade.php
│ │ ├── views.blade.php
│ │ ├── notifications.blade.php
│ │ ├── logs.blade.php
│ │ ├── queries.blade.php
│ │ └── request.blade.php
│ ├── widget.blade.php
│ ├── head.blade.php
│ ├── item.blade.php
│ ├── toolbar.blade.php
│ └── base_js.blade.php
└── css
│ ├── custom.css
│ ├── theme_light.css
│ └── base.css
├── phpunit.xml.dist
├── license.md
├── composer.json
├── .github
└── workflows
│ └── run-tests.yml
├── readme.md
└── config
└── telescope-toolbar.php
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.blade.php linguist-language=PHP
--------------------------------------------------------------------------------
/tests/resources/views/dashboard.blade.php:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.phar
3 | composer.lock
4 | .DS_Store
5 | .phpunit.result.cache
6 | /tests/Browser
7 | /build
--------------------------------------------------------------------------------
/src/helpers.php:
--------------------------------------------------------------------------------
1 | name('telescope-toolbar.baseJs');
6 | Route::get('styling.css', 'ToolbarController@styling')->name('telescope-toolbar.styling');
--------------------------------------------------------------------------------
/src/Http/routes.php:
--------------------------------------------------------------------------------
1 | name('telescope-toolbar.render');
6 | Route::get('show/{token}/{tab?}', 'ToolbarController@show')->name('telescope-toolbar.show');
--------------------------------------------------------------------------------
/resources/icons/dumps.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/resources/icons/session.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/resources/icons/user.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/resources/icons/refresh.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/resources/icons/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/resources/icons/search.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/resources/icons/validator.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/resources/icons/mail.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/resources/icons/queries.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/resources/icons/redirect.svg:
--------------------------------------------------------------------------------
1 |
2 | Redirect
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/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/icons/models.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/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/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/widget.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/resources/icons/commands.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/resources/icons/requests.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/resources/icons/forward.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/resources/icons/menu.svg:
--------------------------------------------------------------------------------
1 |
2 | Menu
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/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/schedule.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/resources/icons/notifications.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/resources/icons/views.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/resources/icons/gates.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/resources/icons/exceptions.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/resources/icons/jobs.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/resources/icons/events.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/resources/icons/logs.svg:
--------------------------------------------------------------------------------
1 |
2 | Logger
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/resources/icons/time.svg:
--------------------------------------------------------------------------------
1 |
2 | Time
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/resources/icons/http-client.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/resources/icons/telescope.svg:
--------------------------------------------------------------------------------
1 |
2 | telescope
3 |
--------------------------------------------------------------------------------
/resources/icons/memory.svg:
--------------------------------------------------------------------------------
1 |
2 | Memory
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/resources/icons/config.svg:
--------------------------------------------------------------------------------
1 |
2 | Config
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/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 |
26 |
27 | @endforeach
28 |
29 | @endslot
30 |
31 | @endcomponent
--------------------------------------------------------------------------------
/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 |
17 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | tests
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/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 |
27 | @endif
28 |
29 |
30 | @endslot
31 |
32 | @endcomponent
33 |
--------------------------------------------------------------------------------
/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/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 |
28 | @endif
29 | @endslot
30 |
31 | @endcomponent
--------------------------------------------------------------------------------
/tests/ToolbarTest.php:
--------------------------------------------------------------------------------
1 | call('GET', 'web/plain');
13 |
14 | $this->assertTrue(Str::contains($crawler->content(), 'Sfjs.loadToolbar'));
15 | $this->assertEquals(200, $crawler->getStatusCode());
16 | }
17 |
18 | public function testItInjectsOnHtml()
19 | {
20 | $crawler = $this->call('GET', 'web/html');
21 |
22 | $this->assertTrue(Str::contains($crawler->content(), 'Sfjs.loadToolbar'));
23 | $this->assertEquals(200, $crawler->getStatusCode());
24 | }
25 |
26 | public function testItDoesntInjectOnJson()
27 | {
28 | $crawler = $this->call('GET', 'api/ping');
29 |
30 | $this->assertFalse(Str::contains($crawler->content(), 'Sfjs.loadToolbar'));
31 | $this->assertEquals(200, $crawler->getStatusCode());
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/resources/icons/redis.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/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.
--------------------------------------------------------------------------------
/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 |
40 |
41 | @endslot
42 |
43 | @endcomponent
--------------------------------------------------------------------------------
/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/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 |
17 |
18 |
34 |
35 | @endslot
36 | @endcomponent
37 |
--------------------------------------------------------------------------------
/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 |
34 |
35 | @endslot
36 | @endcomponent
37 |
--------------------------------------------------------------------------------
/tests/BrowserTestCase.php:
--------------------------------------------------------------------------------
1 | loadMigrationsFrom(__DIR__ . '/../vendor/laravel/telescope/database/migrations');
23 | }
24 |
25 | /**
26 | * Get package providers.
27 | *
28 | * @param \Illuminate\Foundation\Application $app
29 | *
30 | * @return array
31 | */
32 | protected function getPackageProviders($app)
33 | {
34 | return [TelescopeServiceProvider::class, ToolbarServiceProvider::class];
35 | }
36 |
37 | /**
38 | * Get package aliases.
39 | *
40 | * @param \Illuminate\Foundation\Application $app
41 | *
42 | * @return array
43 | */
44 | protected function getPackageAliases($app)
45 | {
46 | return ['Toolbar' => Toolbar::class];
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/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/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 |
47 |
48 | @endslot
49 |
50 | @endcomponent
--------------------------------------------------------------------------------
/resources/views/toolbar.blade.php:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 | @ttIcon('laravel')
9 |
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 |
27 | @ttIcon('close')
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/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 |
50 | @endslot
51 | @endcomponent
--------------------------------------------------------------------------------
/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 |
48 | @endslot
49 |
50 | @endcomponent
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
53 |
54 | @endslot
55 |
56 | @endcomponent
--------------------------------------------------------------------------------
/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 |
55 |
56 | @endslot
57 |
58 | @endcomponent
--------------------------------------------------------------------------------
/resources/icons/laravel.svg:
--------------------------------------------------------------------------------
1 |
2 | Logomark
3 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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 |
71 | @endslot
72 |
73 |
74 | @endcomponent
75 |
--------------------------------------------------------------------------------
/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 |
61 |
62 | @endslot
63 |
64 | @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 |
84 | @endslot
85 |
86 |
87 | @endcomponent
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | Toolbar::class]);
33 | }
34 |
35 | /**
36 | * Define environment setup.
37 | *
38 | * @param \Illuminate\Foundation\Application $app
39 | *
40 | * @return void
41 | */
42 | protected function getEnvironmentSetUp($app)
43 | {
44 | parent::getEnvironmentSetUp($app);
45 |
46 | /** @var Router $router */
47 | $router = $app['router'];
48 |
49 | $this->addWebRoutes($router);
50 | $this->addApiRoutes($router);
51 | $this->addViewPaths();
52 | }
53 |
54 | /**
55 | * @param Router $router
56 | */
57 | protected function addWebRoutes(Router $router)
58 | {
59 | $router->get('web/plain', [
60 | 'uses' => function () {
61 | return 'PONG';
62 | }
63 | ]);
64 |
65 | $router->get('web/html', [
66 | 'uses' => function () {
67 | return 'Pong';
68 | }
69 | ]);
70 | }
71 |
72 | /**
73 | * @param Router $router
74 | */
75 | protected function addApiRoutes(Router $router)
76 | {
77 | $router->get('api/ping', [
78 | 'uses' => function () {
79 | return response()->json(['status' => 'pong']);
80 | }
81 | ]);
82 | }
83 |
84 | protected function addViewPaths()
85 | {
86 | config(['view.paths' => array_merge(config('view.paths'), [__DIR__ . '/resources/views'])]);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/tests/ToolbarBrowserTest.php:
--------------------------------------------------------------------------------
1 | set('app.debug', true);
21 |
22 | /** @var Router $router */
23 | $router = $app['router'];
24 |
25 | $this->addWebRoutes($router);
26 | $this->addApiRoutes($router);
27 |
28 | // \Orchestra\Testbench\Dusk\Options::withoutUI();
29 | }
30 |
31 | /**
32 | * @param Router $router
33 | */
34 | protected function addWebRoutes(Router $router)
35 | {
36 | $router->get('web/plain', [
37 | 'uses' => function () {
38 | return 'PONG';
39 | }
40 | ]);
41 |
42 | $router->get('web/html', [
43 | 'uses' => function () {
44 | return 'HTMLPONG';
45 | }
46 | ]);
47 | }
48 |
49 | /**
50 | * @param Router $router
51 | */
52 | protected function addApiRoutes(Router $router)
53 | {
54 | $router->get('api/ping', [
55 | 'uses' => function () {
56 | return response()->json(['status' => 'pong']);
57 | }
58 | ]);
59 | }
60 |
61 | public function testItInjectsOnPlainText()
62 | {
63 | $this->browse(function ($browser) {
64 | $browser->visit('web/plain')
65 | ->assertSee('PONG')
66 | ->waitFor('.sf-toolbar-block')
67 | ->assertSee('GET /web/plain');
68 | });
69 | }
70 |
71 | public function testItInjectsOnHtml()
72 | {
73 | $this->browse(function ($browser) {
74 | $browser->visit('web/html')
75 | ->assertSee('HTMLPONG')
76 | ->waitFor('.sf-toolbar-block')
77 | ->assertSee('GET /web/html');
78 | });
79 | }
80 |
81 | public function testItDoesntInjectOnJson()
82 | {
83 | $this->browse(function ($browser) {
84 | $browser->visit('api/ping')
85 | ->assertSee('pong')
86 | ->assertSourceMissing('loadToolbar')
87 | ->assertDontSee('GET api/ping');
88 | });
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/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 |
68 |
69 | @endslot
70 |
71 | @endcomponent
72 |
--------------------------------------------------------------------------------
/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 |
72 | @endslot
73 |
74 | @endcomponent
75 |
--------------------------------------------------------------------------------
/tests/TelescopeFeatureTestCase.php:
--------------------------------------------------------------------------------
1 | detectEnvironment(function () {
56 | return 'self-testing';
57 | });
58 | }
59 |
60 | /**
61 | * @param \Illuminate\Foundation\Application $app
62 | * @return void
63 | */
64 | protected function getEnvironmentSetUp($app)
65 | {
66 | $config = $app->get('config');
67 |
68 | $config->set('logging.default', 'errorlog');
69 |
70 | $config->set('database.default', 'testbench');
71 |
72 | $config->set('telescope.storage.database.connection', 'testbench');
73 |
74 | $config->set('database.connections.testbench', [
75 | 'driver' => 'sqlite',
76 | 'database' => ':memory:',
77 | 'prefix' => '',
78 | ]);
79 |
80 | $app->when(DatabaseEntriesRepository::class)
81 | ->needs('$connection')
82 | ->give('testbench');
83 | }
84 |
85 | protected function loadTelescopeEntries()
86 | {
87 | $this->terminateTelescope();
88 |
89 | return EntryModel::all();
90 | }
91 |
92 | public function terminateTelescope()
93 | {
94 | Telescope::store(app(EntriesRepository::class));
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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
677 |
678 |
679 |
--------------------------------------------------------------------------------