{"owner":"filamentphp","repo":"filament","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["CLAUDE.md"],"skills":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nFilament is a full-stack UI framework for Laravel built with Livewire. It provides admin panels, forms, tables, notifications, actions, infolists, and widgets as composable packages.\n\n## Critical: Naming Conventions\n\n### Variable Names\n\n**Never use abbreviated variable names.** Use full descriptive names:\n\n```php\n// GOOD\n$exception, $component, $response, $configuration, $record, $livewire\n\n// BAD - never do this\n$e, $comp, $res, $cfg, $rec, $lw\n```\n\nOnly exception: universally understood abbreviations like `$id`, `$url`.\n\n### Pest Test Names\n\n**Always use backticks for code references. Add `()` for methods:**\n\n```php\n// GOOD\nit('can use `aspectRatio()` to force image cropping')\nit('returns `null` for `getImageCropAspectRatio()` by default')\nit('validates `$record` is an instance of `Model`')\n\n// BAD - missing backticks\nit('can use aspectRatio to force image cropping')\nit('returns null for getImageCropAspectRatio by default')\n```\n\n### Code Comments\n\n**Use backticks when referencing code in comments:**\n\n```php\n// GOOD\n// Uses `evaluate()` to resolve the `Closure`\n// Returns `null` if the `$record` is not set\n\n// BAD\n// Uses evaluate() to resolve the Closure\n```\n\n## Development Commands\n\n**Always update tests when making changes.** For UI components, add browser tests using Pest Browser with `visit()`. Always call `assertNoAccessibilityIssues()` in both light and dark modes (`->inDarkMode()`).\n\n```bash\ncomposer test              # Run all tests (SQLite + commands + PHPStan)\ncomposer test:sqlite       # Run tests with SQLite\ncomposer test:mysql        # Run tests with MySQL\ncomposer test:pgsql        # Run tests with PostgreSQL\ncomposer test:phpstan      # Run PHPStan static analysis\ncomposer cs                # Run all code style fixes (Rector + Pint + Prettier)\n\nnpm run build              # Build all JS and CSS\nnpm run build-demo         # Build and publish to ../demo if it exists\n\n# Run a single test file\nvendor/bin/pest tests/src/Forms/Components/FileUploadTest.php\n\n# Run a single test by name\nvendor/bin/pest --filter=\"it can use \\`aspectRatio\\(\\)\\` to force image cropping\"\n```\n\n## Coding Patterns\n\n### Fluent API\n\nComponents use `make()` constructor and fluent chainable methods. Nullable properties have nullable setters so they can be undone:\n\n```php\nTextInput::make('name')\n    ->label('Full name')\n    ->icon('heroicon-o-user')\n\n// Property and setter share the same name, nullable to allow unsetting\nprotected string | Closure | null $icon = null;\n\npublic function icon(string | Closure | null $icon): static\n{\n    $this->icon = $icon;\n\n    return $this;\n}\n\n// Getter prefixed with `get`, uses `evaluate()` for `Closure` support\npublic function getIcon(): ?string\n{\n    return $this->evaluate($this->icon);\n}\n```\n\n### Boolean Methods\n\n```php\n// Property - `is`/`should`/`can`/`has` prefix, defaults `false`, supports `Closure`\nprotected bool | Closure $isDisabled = false;\n\n// Setter - verb form, defaults `true`, pass `false` to undo\npublic function disabled(bool | Closure $condition = true): static\n{\n    $this->isDisabled = $condition;\n\n    return $this;\n}\n\n// Getter - cast to `bool`\npublic function isDisabled(): bool\n{\n    return (bool) $this->evaluate($this->isDisabled);\n}\n```\n\n### Static Closures\n\nUse `static fn` when the closure doesn't use `$this`:\n\n```php\n->placeholder(static fn (Select $component): ?string => $component->isDisabled() ? null : 'Select...')\n->visible(fn (): bool => $this->canView()) // Uses `$this`, cannot be static\n```\n\n### Container Resolution\n\nUse `app()` instead of `new` to allow users to bind custom implementations:\n\n```php\napp(RelationshipJoiner::class)->prepareQuery($relationship) // Good\n(new RelationshipJoiner())->prepareQuery($relationship)     // Avoid\n```\n\n### Extensibility\n\nDo not use `final` or `readonly` classes - users need to extend Filament classes.\n\n### Concerns and Contracts\n\nTraits in `Concerns/` directories: `Can*` (capabilities), `Has*` (properties).\nInterfaces in `Contracts/` directories.\n\n## Coding Standards\n\n### PHPDoc\n\nOnly add when providing type info beyond native PHP types:\n\n```php\n/** @var array<string, array{label: string, icon: string}> */  // Good\n/** @param string $name The name */                            // Redundant\n```\n\n### Deprecations\n\nKeep old public methods used in docs, mark deprecated:\n\n```php\n/** @deprecated Use `newMethod()` instead. */\npublic function oldMethod(): void\n{\n    return $this->newMethod();\n}\n```\n\n## Architecture\n\n### Packages (`packages/`)\n\nCore: **support** (base utilities) → **schemas** (UI layouts) → **forms**, **infolists**, **tables**, **actions**, **notifications**, **widgets** → **panels** (full admin framework)\n\nOther: query-builder, upgrade, spatie-laravel-media-library-plugin, spatie-laravel-settings-plugin, spatie-laravel-tags-plugin, spatie-laravel-google-fonts-plugin, spark-billing-provider\n\n### Key Classes\n\n- **Resources** (`packages/panels/src/Resources/`): CRUD interfaces for Eloquent models\n- **Pages** (`packages/panels/src/Pages/`): Livewire page components\n- **Schema Components** (`packages/schemas/src/Components/`): Base UI components\n- **Actions** (`packages/actions/src/`): Modal-based operations\n- **Panel** (`packages/panels/src/Panel.php`): Admin panel configuration\n\n### File Locations\n\n- Tests: `tests/src/{Forms,Tables,Actions,Panels}/`\n- Docs: `docs/` and `packages/{package}/docs/`\n- Views: `packages/{package}/resources/views/`\n- CSS: `packages/{package}/resources/css/`\n- Translations: `packages/{package}/resources/lang/{locale}/`\n\n### CSS Hook Classes\n\n**Never use Tailwind classes directly in Blade views.** All Tailwind classes must be in CSS files using `@apply`:\n\n```css\n.fi-fo-field {\n    @apply grid gap-y-2;\n}\n```\n\nHook class naming:\n- Prefix: `fi-` with package codes (`fi-fo-` forms, `fi-ta-` tables, `fi-ac-` actions, etc.)\n- Abbreviations: `btn`, `col`, `ctn`, `wrp`\n\n## Writing Documentation\n\n**Always update documentation for user-facing features** in `packages/{package}/docs/`.\n\n- **Tone**: Direct, second person (\"You may set...\", \"You can do this using...\")\n- **Structure**: Start with `## Introduction`, show simplest code first\n- **Headings**: Use gerunds (\"Setting the type\" not \"Type settings\", \"Enabling search\" not \"Search\")\n- **Formatting**: Backticks for code (`method()`, `ClassName`), include `use` statements\n- **Asides**: `<Aside variant=\"tip|info|danger\">...</Aside>`\n\n### Documentation Screenshots\n\nScreenshots are in `docs-assets/screenshots/`. To add new screenshots:\n\n1. **Add component examples** to the appropriate Livewire component in `docs-assets/app/app/Livewire/` (e.g., `Schemas/LayoutDemo.php`). Give each example a unique `->id()` for the selector:\n   ```php\n   Group::make()\n       ->id('myComponent')\n       ->extraAttributes(['class' => 'p-16 max-w-2xl'])\n       ->schema([\n           // Your component here\n       ]),\n   ```\n\n2. **Add screenshot definitions** to `docs-assets/screenshots/schema.js`:\n   ```js\n   'schemas/layout/my-component/simple': {\n       url: 'schemas/layout',\n       selector: '#myComponent',\n       viewport: { width: 1920, height: 640, deviceScaleFactor: 3 },\n   },\n   ```\n\n3. **Build assets** if you changed any CSS or JS files. Two builds are required — the repo root compiles each package's dist output, and the docs app has its own Vite build that bundles those outputs into `docs-assets/app/public/build/`. Skipping the second step leaves the docs app serving stale CSS, and screenshots will render against pre-change styles:\n   ```bash\n   # Terminal 1: compile package dist output\n   npm run build\n\n   # Terminal 2: bundle the docs app's CSS from the package output\n   cd docs-assets/app && npm run build\n   ```\n   If you also changed the Livewire demo or Blade views, clear caches afterwards: `cd docs-assets/app && php artisan optimize:clear`.\n\n4. **Generate screenshots**:\n   ```bash\n   # Terminal 1: Start the app server (must use default port 8000)\n   cd docs-assets/app && php artisan serve\n\n   # Terminal 2: Run from the screenshots directory\n   cd docs-assets/screenshots\n   export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true\n   export PUPPETEER_EXECUTABLE_PATH=$(which chromium)\n   node script.js \"schemas/layout/my-component/*\"  # Filter pattern\n   node script.js --parallel \"tables/*\"            # Process in parallel (add =N for a specific worker count)\n   ```\n\n   **Important:** The script expects `http://127.0.0.1:8000`. Don't use a custom port.\n\n   **Important:** Set up the app first with `php artisan migrate:fresh --seed && php artisan storage:link` — seeding also copies the file upload demos' sample images from `database/seed-images/` to the `public` disk.\n\n   `--parallel` starts its own servers on ports 8001+ (no `php artisan serve` needed), each with its own copy of the seeded database, because many demos mutate the database on mount and would corrupt each other's screenshots if they shared one. The pristine database is restored before every page load, so each screenshot always sees freshly seeded data — this makes parallel mode the most reliable way to run large batches (in serial mode, a demo that truncates tables can 404 later entries, e.g. tenancy or resource pages, until you reseed). Screenshots using the `configure` option run serially after the parallel pool because they mutate a PHP file shared by every server.\n\n   Schema entries without a `before` callback that share the same URL, viewport, and theme are captured from a single page load, and one browser process is shared across the whole run. If an entry needs an isolated page load (e.g. its demo mutates state when rendered), give it a `before` callback.\n\n   Pages are captured with `prefers-reduced-motion: reduce` and with CSS animations, transitions, and the input caret disabled, so screenshots never depend on which animation frame they caught (e.g. spinning loading indicators, modal fade-ins, caret blinking).\n\n5. **Use in docs** with `<AutoScreenshot name=\"schemas/layout/my-component/simple\" alt=\"Description\" version=\"4.x\" />`\n\nScreenshots are generated in `images/light/` and `images/dark/`. Use natural, realistic content - not test-like examples.\n"},"files":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nFilament is a full-stack UI framework for Laravel built with Livewire. It provides admin panels, forms, tables, notifications, actions, infolists, and widgets as composable packages.\n\n## Critical: Naming Conventions\n\n### Variable Names\n\n**Never use abbreviated variable names.** Use full descriptive names:\n\n```php\n// GOOD\n$exception, $component, $response, $configuration, $record, $livewire\n\n// BAD - never do this\n$e, $comp, $res, $cfg, $rec, $lw\n```\n\nOnly exception: universally understood abbreviations like `$id`, `$url`.\n\n### Pest Test Names\n\n**Always use backticks for code references. Add `()` for methods:**\n\n```php\n// GOOD\nit('can use `aspectRatio()` to force image cropping')\nit('returns `null` for `getImageCropAspectRatio()` by default')\nit('validates `$record` is an instance of `Model`')\n\n// BAD - missing backticks\nit('can use aspectRatio to force image cropping')\nit('returns null for getImageCropAspectRatio by default')\n```\n\n### Code Comments\n\n**Use backticks when referencing code in comments:**\n\n```php\n// GOOD\n// Uses `evaluate()` to resolve the `Closure`\n// Returns `null` if the `$record` is not set\n\n// BAD\n// Uses evaluate() to resolve the Closure\n```\n\n## Development Commands\n\n**Always update tests when making changes.** For UI components, add browser tests using Pest Browser with `visit()`. Always call `assertNoAccessibilityIssues()` in both light and dark modes (`->inDarkMode()`).\n\n```bash\ncomposer test              # Run all tests (SQLite + commands + PHPStan)\ncomposer test:sqlite       # Run tests with SQLite\ncomposer test:mysql        # Run tests with MySQL\ncomposer test:pgsql        # Run tests with PostgreSQL\ncomposer test:phpstan      # Run PHPStan static analysis\ncomposer cs                # Run all code style fixes (Rector + Pint + Prettier)\n\nnpm run build              # Build all JS and CSS\nnpm run build-demo         # Build and publish to ../demo if it exists\n\n# Run a single test file\nvendor/bin/pest tests/src/Forms/Components/FileUploadTest.php\n\n# Run a single test by name\nvendor/bin/pest --filter=\"it can use \\`aspectRatio\\(\\)\\` to force image cropping\"\n```\n\n## Coding Patterns\n\n### Fluent API\n\nComponents use `make()` constructor and fluent chainable methods. Nullable properties have nullable setters so they can be undone:\n\n```php\nTextInput::make('name')\n    ->label('Full name')\n    ->icon('heroicon-o-user')\n\n// Property and setter share the same name, nullable to allow unsetting\nprotected string | Closure | null $icon = null;\n\npublic function icon(string | Closure | null $icon): static\n{\n    $this->icon = $icon;\n\n    return $this;\n}\n\n// Getter prefixed with `get`, uses `evaluate()` for `Closure` support\npublic function getIcon(): ?string\n{\n    return $this->evaluate($this->icon);\n}\n```\n\n### Boolean Methods\n\n```php\n// Property - `is`/`should`/`can`/`has` prefix, defaults `false`, supports `Closure`\nprotected bool | Closure $isDisabled = false;\n\n// Setter - verb form, defaults `true`, pass `false` to undo\npublic function disabled(bool | Closure $condition = true): static\n{\n    $this->isDisabled = $condition;\n\n    return $this;\n}\n\n// Getter - cast to `bool`\npublic function isDisabled(): bool\n{\n    return (bool) $this->evaluate($this->isDisabled);\n}\n```\n\n### Static Closures\n\nUse `static fn` when the closure doesn't use `$this`:\n\n```php\n->placeholder(static fn (Select $component): ?string => $component->isDisabled() ? null : 'Select...')\n->visible(fn (): bool => $this->canView()) // Uses `$this`, cannot be static\n```\n\n### Container Resolution\n\nUse `app()` instead of `new` to allow users to bind custom implementations:\n\n```php\napp(RelationshipJoiner::class)->prepareQuery($relationship) // Good\n(new RelationshipJoiner())->prepareQuery($relationship)     // Avoid\n```\n\n### Extensibility\n\nDo not use `final` or `readonly` classes - users need to extend Filament classes.\n\n### Concerns and Contracts\n\nTraits in `Concerns/` directories: `Can*` (capabilities), `Has*` (properties).\nInterfaces in `Contracts/` directories.\n\n## Coding Standards\n\n### PHPDoc\n\nOnly add when providing type info beyond native PHP types:\n\n```php\n/** @var array<string, array{label: string, icon: string}> */  // Good\n/** @param string $name The name */                            // Redundant\n```\n\n### Deprecations\n\nKeep old public methods used in docs, mark deprecated:\n\n```php\n/** @deprecated Use `newMethod()` instead. */\npublic function oldMethod(): void\n{\n    return $this->newMethod();\n}\n```\n\n## Architecture\n\n### Packages (`packages/`)\n\nCore: **support** (base utilities) → **schemas** (UI layouts) → **forms**, **infolists**, **tables**, **actions**, **notifications**, **widgets** → **panels** (full admin framework)\n\nOther: query-builder, upgrade, spatie-laravel-media-library-plugin, spatie-laravel-settings-plugin, spatie-laravel-tags-plugin, spatie-laravel-google-fonts-plugin, spark-billing-provider\n\n### Key Classes\n\n- **Resources** (`packages/panels/src/Resources/`): CRUD interfaces for Eloquent models\n- **Pages** (`packages/panels/src/Pages/`): Livewire page components\n- **Schema Components** (`packages/schemas/src/Components/`): Base UI components\n- **Actions** (`packages/actions/src/`): Modal-based operations\n- **Panel** (`packages/panels/src/Panel.php`): Admin panel configuration\n\n### File Locations\n\n- Tests: `tests/src/{Forms,Tables,Actions,Panels}/`\n- Docs: `docs/` and `packages/{package}/docs/`\n- Views: `packages/{package}/resources/views/`\n- CSS: `packages/{package}/resources/css/`\n- Translations: `packages/{package}/resources/lang/{locale}/`\n\n### CSS Hook Classes\n\n**Never use Tailwind classes directly in Blade views.** All Tailwind classes must be in CSS files using `@apply`:\n\n```css\n.fi-fo-field {\n    @apply grid gap-y-2;\n}\n```\n\nHook class naming:\n- Prefix: `fi-` with package codes (`fi-fo-` forms, `fi-ta-` tables, `fi-ac-` actions, etc.)\n- Abbreviations: `btn`, `col`, `ctn`, `wrp`\n\n## Writing Documentation\n\n**Always update documentation for user-facing features** in `packages/{package}/docs/`.\n\n- **Tone**: Direct, second person (\"You may set...\", \"You can do this using...\")\n- **Structure**: Start with `## Introduction`, show simplest code first\n- **Headings**: Use gerunds (\"Setting the type\" not \"Type settings\", \"Enabling search\" not \"Search\")\n- **Formatting**: Backticks for code (`method()`, `ClassName`), include `use` statements\n- **Asides**: `<Aside variant=\"tip|info|danger\">...</Aside>`\n\n### Documentation Screenshots\n\nScreenshots are in `docs-assets/screenshots/`. To add new screenshots:\n\n1. **Add component examples** to the appropriate Livewire component in `docs-assets/app/app/Livewire/` (e.g., `Schemas/LayoutDemo.php`). Give each example a unique `->id()` for the selector:\n   ```php\n   Group::make()\n       ->id('myComponent')\n       ->extraAttributes(['class' => 'p-16 max-w-2xl'])\n       ->schema([\n           // Your component here\n       ]),\n   ```\n\n2. **Add screenshot definitions** to `docs-assets/screenshots/schema.js`:\n   ```js\n   'schemas/layout/my-component/simple': {\n       url: 'schemas/layout',\n       selector: '#myComponent',\n       viewport: { width: 1920, height: 640, deviceScaleFactor: 3 },\n   },\n   ```\n\n3. **Build assets** if you changed any CSS or JS files. Two builds are required — the repo root compiles each package's dist output, and the docs app has its own Vite build that bundles those outputs into `docs-assets/app/public/build/`. Skipping the second step leaves the docs app serving stale CSS, and screenshots will render against pre-change styles:\n   ```bash\n   # Terminal 1: compile package dist output\n   npm run build\n\n   # Terminal 2: bundle the docs app's CSS from the package output\n   cd docs-assets/app && npm run build\n   ```\n   If you also changed the Livewire demo or Blade views, clear caches afterwards: `cd docs-assets/app && php artisan optimize:clear`.\n\n4. **Generate screenshots**:\n   ```bash\n   # Terminal 1: Start the app server (must use default port 8000)\n   cd docs-assets/app && php artisan serve\n\n   # Terminal 2: Run from the screenshots directory\n   cd docs-assets/screenshots\n   export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true\n   export PUPPETEER_EXECUTABLE_PATH=$(which chromium)\n   node script.js \"schemas/layout/my-component/*\"  # Filter pattern\n   node script.js --parallel \"tables/*\"            # Process in parallel (add =N for a specific worker count)\n   ```\n\n   **Important:** The script expects `http://127.0.0.1:8000`. Don't use a custom port.\n\n   **Important:** Set up the app first with `php artisan migrate:fresh --seed && php artisan storage:link` — seeding also copies the file upload demos' sample images from `database/seed-images/` to the `public` disk.\n\n   `--parallel` starts its own servers on ports 8001+ (no `php artisan serve` needed), each with its own copy of the seeded database, because many demos mutate the database on mount and would corrupt each other's screenshots if they shared one. The pristine database is restored before every page load, so each screenshot always sees freshly seeded data — this makes parallel mode the most reliable way to run large batches (in serial mode, a demo that truncates tables can 404 later entries, e.g. tenancy or resource pages, until you reseed). Screenshots using the `configure` option run serially after the parallel pool because they mutate a PHP file shared by every server.\n\n   Schema entries without a `before` callback that share the same URL, viewport, and theme are captured from a single page load, and one browser process is shared across the whole run. If an entry needs an isolated page load (e.g. its demo mutates state when rendered), give it a `before` callback.\n\n   Pages are captured with `prefers-reduced-motion: reduce` and with CSS animations, transitions, and the input caret disabled, so screenshots never depend on which animation frame they caught (e.g. spinning loading indicators, modal fade-ins, caret blinking).\n\n5. **Use in docs** with `<AutoScreenshot name=\"schemas/layout/my-component/simple\" alt=\"Description\" version=\"4.x\" />`\n\nScreenshots are generated in `images/light/` and `images/dark/`. Use natural, realistic content - not test-like examples.\n"},"items":[{"name":"CLAUDE.md","path":"CLAUDE.md","title":"CLAUDE.md","content":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nFilament is a full-stack UI framework for Laravel built with Livewire. It provides admin panels, forms, tables, notifications, actions, infolists, and widgets as composable packages.\n\n## Critical: Naming Conventions\n\n### Variable Names\n\n**Never use abbreviated variable names.** Use full descriptive names:\n\n```php\n// GOOD\n$exception, $component, $response, $configuration, $record, $livewire\n\n// BAD - never do this\n$e, $comp, $res, $cfg, $rec, $lw\n```\n\nOnly exception: universally understood abbreviations like `$id`, `$url`.\n\n### Pest Test Names\n\n**Always use backticks for code references. Add `()` for methods:**\n\n```php\n// GOOD\nit('can use `aspectRatio()` to force image cropping')\nit('returns `null` for `getImageCropAspectRatio()` by default')\nit('validates `$record` is an instance of `Model`')\n\n// BAD - missing backticks\nit('can use aspectRatio to force image cropping')\nit('returns null for getImageCropAspectRatio by default')\n```\n\n### Code Comments\n\n**Use backticks when referencing code in comments:**\n\n```php\n// GOOD\n// Uses `evaluate()` to resolve the `Closure`\n// Returns `null` if the `$record` is not set\n\n// BAD\n// Uses evaluate() to resolve the Closure\n```\n\n## Development Commands\n\n**Always update tests when making changes.** For UI components, add browser tests using Pest Browser with `visit()`. Always call `assertNoAccessibilityIssues()` in both light and dark modes (`->inDarkMode()`).\n\n```bash\ncomposer test              # Run all tests (SQLite + commands + PHPStan)\ncomposer test:sqlite       # Run tests with SQLite\ncomposer test:mysql        # Run tests with MySQL\ncomposer test:pgsql        # Run tests with PostgreSQL\ncomposer test:phpstan      # Run PHPStan static analysis\ncomposer cs                # Run all code style fixes (Rector + Pint + Prettier)\n\nnpm run build              # Build all JS and CSS\nnpm run build-demo         # Build and publish to ../demo if it exists\n\n# Run a single test file\nvendor/bin/pest tests/src/Forms/Components/FileUploadTest.php\n\n# Run a single test by name\nvendor/bin/pest --filter=\"it can use \\`aspectRatio\\(\\)\\` to force image cropping\"\n```\n\n## Coding Patterns\n\n### Fluent API\n\nComponents use `make()` constructor and fluent chainable methods. Nullable properties have nullable setters so they can be undone:\n\n```php\nTextInput::make('name')\n    ->label('Full name')\n    ->icon('heroicon-o-user')\n\n// Property and setter share the same name, nullable to allow unsetting\nprotected string | Closure | null $icon = null;\n\npublic function icon(string | Closure | null $icon): static\n{\n    $this->icon = $icon;\n\n    return $this;\n}\n\n// Getter prefixed with `get`, uses `evaluate()` for `Closure` support\npublic function getIcon(): ?string\n{\n    return $this->evaluate($this->icon);\n}\n```\n\n### Boolean Methods\n\n```php\n// Property - `is`/`should`/`can`/`has` prefix, defaults `false`, supports `Closure`\nprotected bool | Closure $isDisabled = false;\n\n// Setter - verb form, defaults `true`, pass `false` to undo\npublic function disabled(bool | Closure $condition = true): static\n{\n    $this->isDisabled = $condition;\n\n    return $this;\n}\n\n// Getter - cast to `bool`\npublic function isDisabled(): bool\n{\n    return (bool) $this->evaluate($this->isDisabled);\n}\n```\n\n### Static Closures\n\nUse `static fn` when the closure doesn't use `$this`:\n\n```php\n->placeholder(static fn (Select $component): ?string => $component->isDisabled() ? null : 'Select...')\n->visible(fn (): bool => $this->canView()) // Uses `$this`, cannot be static\n```\n\n### Container Resolution\n\nUse `app()` instead of `new` to allow users to bind custom implementations:\n\n```php\napp(RelationshipJoiner::class)->prepareQuery($relationship) // Good\n(new RelationshipJoiner())->prepareQuery($relationship)     // Avoid\n```\n\n### Extensibility\n\nDo not use `final` or `readonly` classes - users need to extend Filament classes.\n\n### Concerns and Contracts\n\nTraits in `Concerns/` directories: `Can*` (capabilities), `Has*` (properties).\nInterfaces in `Contracts/` directories.\n\n## Coding Standards\n\n### PHPDoc\n\nOnly add when providing type info beyond native PHP types:\n\n```php\n/** @var array<string, array{label: string, icon: string}> */  // Good\n/** @param string $name The name */                            // Redundant\n```\n\n### Deprecations\n\nKeep old public methods used in docs, mark deprecated:\n\n```php\n/** @deprecated Use `newMethod()` instead. */\npublic function oldMethod(): void\n{\n    return $this->newMethod();\n}\n```\n\n## Architecture\n\n### Packages (`packages/`)\n\nCore: **support** (base utilities) → **schemas** (UI layouts) → **forms**, **infolists**, **tables**, **actions**, **notifications**, **widgets** → **panels** (full admin framework)\n\nOther: query-builder, upgrade, spatie-laravel-media-library-plugin, spatie-laravel-settings-plugin, spatie-laravel-tags-plugin, spatie-laravel-google-fonts-plugin, spark-billing-provider\n\n### Key Classes\n\n- **Resources** (`packages/panels/src/Resources/`): CRUD interfaces for Eloquent models\n- **Pages** (`packages/panels/src/Pages/`): Livewire page components\n- **Schema Components** (`packages/schemas/src/Components/`): Base UI components\n- **Actions** (`packages/actions/src/`): Modal-based operations\n- **Panel** (`packages/panels/src/Panel.php`): Admin panel configuration\n\n### File Locations\n\n- Tests: `tests/src/{Forms,Tables,Actions,Panels}/`\n- Docs: `docs/` and `packages/{package}/docs/`\n- Views: `packages/{package}/resources/views/`\n- CSS: `packages/{package}/resources/css/`\n- Translations: `packages/{package}/resources/lang/{locale}/`\n\n### CSS Hook Classes\n\n**Never use Tailwind classes directly in Blade views.** All Tailwind classes must be in CSS files using `@apply`:\n\n```css\n.fi-fo-field {\n    @apply grid gap-y-2;\n}\n```\n\nHook class naming:\n- Prefix: `fi-` with package codes (`fi-fo-` forms, `fi-ta-` tables, `fi-ac-` actions, etc.)\n- Abbreviations: `btn`, `col`, `ctn`, `wrp`\n\n## Writing Documentation\n\n**Always update documentation for user-facing features** in `packages/{package}/docs/`.\n\n- **Tone**: Direct, second person (\"You may set...\", \"You can do this using...\")\n- **Structure**: Start with `## Introduction`, show simplest code first\n- **Headings**: Use gerunds (\"Setting the type\" not \"Type settings\", \"Enabling search\" not \"Search\")\n- **Formatting**: Backticks for code (`method()`, `ClassName`), include `use` statements\n- **Asides**: `<Aside variant=\"tip|info|danger\">...</Aside>`\n\n### Documentation Screenshots\n\nScreenshots are in `docs-assets/screenshots/`. To add new screenshots:\n\n1. **Add component examples** to the appropriate Livewire component in `docs-assets/app/app/Livewire/` (e.g., `Schemas/LayoutDemo.php`). Give each example a unique `->id()` for the selector:\n   ```php\n   Group::make()\n       ->id('myComponent')\n       ->extraAttributes(['class' => 'p-16 max-w-2xl'])\n       ->schema([\n           // Your component here\n       ]),\n   ```\n\n2. **Add screenshot definitions** to `docs-assets/screenshots/schema.js`:\n   ```js\n   'schemas/layout/my-component/simple': {\n       url: 'schemas/layout',\n       selector: '#myComponent',\n       viewport: { width: 1920, height: 640, deviceScaleFactor: 3 },\n   },\n   ```\n\n3. **Build assets** if you changed any CSS or JS files. Two builds are required — the repo root compiles each package's dist output, and the docs app has its own Vite build that bundles those outputs into `docs-assets/app/public/build/`. Skipping the second step leaves the docs app serving stale CSS, and screenshots will render against pre-change styles:\n   ```bash\n   # Terminal 1: compile package dist output\n   npm run build\n\n   # Terminal 2: bundle the docs app's CSS from the package output\n   cd docs-assets/app && npm run build\n   ```\n   If you also changed the Livewire demo or Blade views, clear caches afterwards: `cd docs-assets/app && php artisan optimize:clear`.\n\n4. **Generate screenshots**:\n   ```bash\n   # Terminal 1: Start the app server (must use default port 8000)\n   cd docs-assets/app && php artisan serve\n\n   # Terminal 2: Run from the screenshots directory\n   cd docs-assets/screenshots\n   export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true\n   export PUPPETEER_EXECUTABLE_PATH=$(which chromium)\n   node script.js \"schemas/layout/my-component/*\"  # Filter pattern\n   node script.js --parallel \"tables/*\"            # Process in parallel (add =N for a specific worker count)\n   ```\n\n   **Important:** The script expects `http://127.0.0.1:8000`. Don't use a custom port.\n\n   **Important:** Set up the app first with `php artisan migrate:fresh --seed && php artisan storage:link` — seeding also copies the file upload demos' sample images from `database/seed-images/` to the `public` disk.\n\n   `--parallel` starts its own servers on ports 8001+ (no `php artisan serve` needed), each with its own copy of the seeded database, because many demos mutate the database on mount and would corrupt each other's screenshots if they shared one. The pristine database is restored before every page load, so each screenshot always sees freshly seeded data — this makes parallel mode the most reliable way to run large batches (in serial mode, a demo that truncates tables can 404 later entries, e.g. tenancy or resource pages, until you reseed). Screenshots using the `configure` option run serially after the parallel pool because they mutate a PHP file shared by every server.\n\n   Schema entries without a `before` callback that share the same URL, viewport, and theme are captured from a single page load, and one browser process is shared across the whole run. If an entry needs an isolated page load (e.g. its demo mutates state when rendered), give it a `before` callback.\n\n   Pages are captured with `prefers-reduced-motion: reduce` and with CSS animations, transitions, and the input caret disabled, so screenshots never depend on which animation frame they caught (e.g. spinning loading indicators, modal fade-ins, caret blinking).\n\n5. **Use in docs** with `<AutoScreenshot name=\"schemas/layout/my-component/simple\" alt=\"Description\" version=\"4.x\" />`\n\nScreenshots are generated in `images/light/` and `images/dark/`. Use natural, realistic content - not test-like examples.\n","category":"root","tokens":2569}]}