visualbuilder/filament-screenshot-catalogue
| Install | |
|---|---|
composer require visualbuilder/filament-screenshot-catalogue |
|
| Latest Version: | 5.7.0 |
| PHP: | ^8.2 |
| License: | GPL-2.0-or-later |
| Last Updated: | May 31, 2026 |
| Links: | GitHub · Packagist |
Filament Panel Screenshot Catalogue
Capture every page of a Filament v5 panel — at desktop, tablet, and mobile breakpoints, in light and dark mode — upload to S3, and publish a browsable, link-shareable index. Designed as the data source for visual QA, design reviews, marketing assets, and AI-driven visual regression workflows.
What you get
php artisan panel:sitemap --panel=admin # discover every URL
php artisan screenshot:dispatch --panel=admin --tag=latest # queue per-page capture jobs
php artisan screenshot:capture --panel=admin --page=dashboard --tag=latest # one-off sync run
php artisan screenshot:rebuild-index --panel=admin --tag=latest # rebuild index.html only
The pipeline is:
- Sitemap generator walks the panel's resources, custom pages and auth screens, picks a representative record for
view/editURLs, and saves a JSON manifest tostorage/app/sitemap-{panel}.json. - Capture jobs drive Playwright through every entry × every viewport × every mode, save PNGs locally, and upload to your configured S3 disk.
- Index builder lists the run's S3 prefix and renders
index.htmlwith a sticky sidebar nav, scrollspy, lightbox, and one section per page. - Output lives at
s3://{disk}/{prefix}/{env}/{panel}/{version}/with a top-levelindex.htmland{slug}/{viewport}-{mode}.pngper shot.
Install
This is a development tool — most consumers will want it as a require-dev dependency so it never ships to production:
composer require --dev visualbuilder/filament-screenshot-catalogue
(If you genuinely need the artisan commands available in production — e.g. you run captures from a CI job that mounts production-like infrastructure — drop the --dev flag.)
The package's commands auto-register. Then make sure both Composer and Node prerequisites are in place:
| Dependency | Why it's needed | How to install |
|---|---|---|
aws/aws-sdk-php + an S3 disk |
Uploads + serves the catalogue | Already pulled in transitively. Configure an S3 disk in config/filesystems.php (default expected name: s3_public). |
| Node 18+ | Runs the Playwright runner | System-level (apt install nodejs / brew install node / etc.). |
| Playwright | Drives the headless browser | npm install playwright && npx playwright install chromium in the host project root. |
| A canonical "screenshot user" per panel | The runner logs in as this user and the sitemap generator uses them to find sample records | Seeded by the host (see Panel registration below). |
Optionally publish the config, the Node runner, or the bundled Claude Code skill:
php artisan vendor:publish --tag=filament-screenshot-catalogue-config # override defaults
php artisan vendor:publish --tag=filament-screenshot-catalogue-js # custom Playwright runner
php artisan vendor:publish --tag=filament-screenshot-catalogue-claude-skills # /screenshot-catalogue slash command
The Claude skill installs into the host's .claude/commands/screenshot-catalogue.md — gives Claude the full command surface, troubleshooting tips, and the PanelDescriptor registration pattern as context.
Panel registration
Hosts tell the package which panels to capture by registering a PanelDescriptor per panel — typically inside a small service provider:
namespace App\Providers;
use Filament\Facades\Filament;
use Illuminate\Support\ServiceProvider;
use Visualbuilder\FilamentScreenshotCatalogue\PanelDescriptor;
use Visualbuilder\FilamentScreenshotCatalogue\PanelRegistry;
class ScreenshotCatalogueServiceProvider extends ServiceProvider
{
public function boot(): void
{
PanelRegistry::register(new PanelDescriptor(
key: 'admin',
panelId: 'admin',
domain: env('ADMIN_DOMAIN'),
loginHeading: 'Admin Login',
email: env('SCREENSHOT_ADMIN_EMAIL'),
password: env('SCREENSHOT_ADMIN_PASSWORD'),
authenticator: static function (): void {
$user = \App\Models\User::where('email', config('catalogue.admin_email'))->first();
if ($user !== null) auth('web')->setUser($user);
},
));
}
}
Add the provider to bootstrap/providers.php. If you installed the package as require-dev (the default suggestion above), wrap the registration in a class_exists check so production — where the package isn't installed — silently skips it instead of crashing on a missing class import:
// bootstrap/providers.php
$providers = [
// ...your usual providers...
];
if (class_exists(\Visualbuilder\FilamentScreenshotCatalogue\PanelRegistry::class)) {
$providers[] = App\Providers\ScreenshotCatalogueServiceProvider::class;
}
return $providers;
If you installed as a regular require dependency, register the provider unconditionally.
Tenanted panels (Filament's tenancy enabled): set the active tenant inside authenticator:
authenticator: static function (): void {
$user = \App\Models\OrganisationUser::where('email', 'sample@example.com')->first();
if ($user !== null) {
auth('organisation_user')->setUser($user);
Filament::setTenant($user->organisation);
}
},
Configuration
Override defaults by publishing config/screenshot-catalogue.php. Common knobs:
return [
// S3 disk (must be configured in config/filesystems.php).
'disk' => 's3_public',
// Top-level S3 prefix.
'path_prefix' => 'screenshots',
// Capture viewports — name → { name, width, height }. Override to
// standardise on your own breakpoints.
'viewports' => [
'desktop' => ['name' => 'desktop', 'width' => 1280, 'height' => 800],
'tablet' => ['name' => 'tablet', 'width' => 768, 'height' => 1024],
'mobile' => ['name' => 'mobile', 'width' => 375, 'height' => 812],
],
// CSS injected into every page just before the screenshot fires —
// use this to lock host theme animations into a stable state. Empty
// by default.
//
// Example for a theme that animates its topbar on root scroll:
//
// 'capture_time_css' => '
// .fi-topbar { padding-block: 1rem !important; }
// .fi-main { padding-top: 7rem !important; }
// ',
'capture_time_css' => '',
// Sitemap entries to omit from the catalogue. Match the `slug` field
// in `storage/app/sitemap-{panel}.json` — Filament's `{resource}.{page}`
// form (e.g. `users.index`, `orders.edit`) for resource pages, or the
// page class slug for custom pages. NOT URL slugs.
'excluded_slugs' => [],
// Brand assets uploaded next to index.html so the catalogue's heading
// shows your wordmark + favicon. Both are optional.
'brand' => [
'name' => 'Panel Screenshots',
'logo' => public_path('media/your_logo_dark_mode.svg'),
'favicon' => public_path('media/your_favicon.svg'),
],
];
How the catalogue is laid out on S3
{prefix}/{env}/{panel}/{version}/ (env omitted in production)
├── index.html
├── favicon.svg
├── logo.svg
├── dashboard/
│ ├── desktop-light.png
│ ├── desktop-dark.png
│ ├── tablet-light.png
│ ├── tablet-dark.png
│ ├── mobile-light.png
│ └── mobile-dark.png
├── orders.index/
│ └── …
└── …
Tag-versioning: --tag=latest overwrites in place (continuous capture), any other tag (e.g. --tag=v2.5.0) is treated as immutable.
Commands
| Command | Use it for |
|---|---|
panel:sitemap --panel=KEY |
Generate sitemap-{panelId}.json for the panel. Re-run when routes change. |
screenshot:dispatch --panel=KEY --tag=latest |
Queue one CapturePageScreenshotsJob per sitemap entry plus a final RebuildScreenshotIndexJob. Use a queue worker. |
screenshot:capture --panel=KEY --page=SLUG --tag=latest |
Synchronous run, single page or a few. Useful for debug. |
screenshot:rebuild-index --panel=KEY --tag=latest |
Re-render index.html from the existing PNGs without re-capturing. |
All four accept --panel=KEY where KEY is the friendly CLI name registered in the descriptor (e.g. admin). The package translates to Filament's internal panel ID for URL/route resolution.
What's not in the package (host responsibilities)
- The seeded sample user(s) per panel — the package needs a real user to log in as, but creating one is host territory.
- Theme-specific tweaks beyond
capture_time_css(e.g. permanent CSS overrides for a custom design system) — keep those in your theme stylesheet. - Authentication wiring (tenants, magic links, 2FA dismissal) — the descriptor's
authenticatorclosure is the seam.
Production safety
The package refuses to run screenshot:capture and screenshot:dispatch when APP_ENV=production. The dispatch flow respects APP_DEBUG=true too — it bails out so the Laravel debug bar doesn't pollute every shot.
License
GPL-2.0-or-later.