Setup

Plugin Registration

Register TagixoPrimixPlugin in your AdminPanelProvider and understand available options.

Plugin Registration

This document covers how to register TagixoPrimixPlugin inside a Primix panel provider, what is auto-registered on your behalf, and how to configure optional features.


Registering the Plugin

Add TagixoPrimixPlugin::make() to the plugins() call inside your AdminPanelProvider:

use Ccast\TagixoPrimix\TagixoPrimixPlugin;
use Primix\Primix\PrimixPanelProvider;

class AdminPanelProvider extends PrimixPanelProvider
{
    public function panel(Panel $panel): Panel
    {
        return parent::panel($panel)
            ->id('admin')
            ->path('admin')
            ->plugins([
                TagixoPrimixPlugin::make(),
            ]);
    }
}

Important — provider base class Your provider must extend Primix\Primix\PrimixPanelProvider, not Filament\PanelProvider. Using the Filament base class skips Primix boot hooks and will cause the plugin to partially or silently fail.


What Auto-Registers

When TagixoPrimixPlugin::make() is present in the panel, the following are registered automatically without any additional configuration:

What Details
Builder routes /tagixo/builder/* endpoints mounted under the panel path — page save, asset serving, global variables, font preview, form submission proxy
LiVue asset hooks <link> and <script> tags for the LiVue runtime injected via Primix render hooks
enableAppForms Registers Tagixo form schema support globally (see section below)
Notifications bridge Primix flash notifications wired to the Tagixo frontend notification bus
Admin resources Pages, Layouts, Menus, Global Variables, and Forms Primix resources auto-registered in the panel

You do not need to register routes, call Route::tagixo(), or manually boot the builder.


enableAppForms

The plugin calls Tagixo::enableAppForms() during its boot phase. This call:

  • Registers the tgx_forms table as a global form schema source available to all pages and modules
  • Is idempotent — calling it more than once has no effect
  • Is global in scope — not tied to a panel; it affects the full application

Do not call enableAppForms() again in your own service provider. The plugin already handles it. If you call it before the plugin boots, it is safe (idempotent), but the call is redundant.


Fluent Options

withMediaGallery()

TagixoPrimixPlugin::make()
    ->withMediaGallery(),

Adds a Media Gallery resource to the panel (upload, organize, and reuse images and files across pages). When enabled:

  • A Media nav item appears in the admin sidebar
  • The media picker field becomes available inside PropType definitions
  • Uploaded files are stored in the public disk under media/

Without withMediaGallery(): The media picker field falls back to a plain URL text input. No upload or library UI is available.


Boot Sequence

The following ASCII tree shows where the plugin boots relative to the Laravel kernel and Primix panel boot. Understanding this order matters if you add custom service providers or listen to boot events.

Laravel kernel boot
└── AppServiceProvider::boot()
└── TagixoServiceProvider::boot()          ← plugin core (routes, models, migrations)
    └── TagixoPrimixServiceProvider::boot()  ← Primix integration layer registered
        └── PrimixPanelProvider::boot()
            └── TagixoPrimixPlugin::boot()   ← your plugin call runs here
                ├── enableAppForms()
                ├── register builder routes
                ├── register LiVue hooks
                └── register admin resources

Octane note: Under Laravel Octane (Swoole), the boot sequence runs once per worker process, not per request. State set during plugin boot (registered routes, resolved bindings) persists across requests on the same worker. Avoid storing per-request state in the plugin's boot() callback or in service provider properties.


Multi-Panel Setups

If your application has two panels (for example, an admin panel and a vendor panel), register TagixoPrimixPlugin only in the panel where the builder should appear:

// AdminPanelProvider.php — builder panel
class AdminPanelProvider extends PrimixPanelProvider
{
    public function panel(Panel $panel): Panel
    {
        return parent::panel($panel)
            ->id('admin')
            ->path('admin')
            ->plugins([
                TagixoPrimixPlugin::make()->withMediaGallery(),
            ]);
    }
}

// VendorPanelProvider.php — separate panel, no builder
class VendorPanelProvider extends PrimixPanelProvider
{
    public function panel(Panel $panel): Panel
    {
        return parent::panel($panel)
            ->id('vendor')
            ->path('vendor')
            ->plugins([
                // TagixoPrimixPlugin not included here
            ]);
    }
}

Route scoping: Builder routes (/tagixo/builder/*) are scoped to the panel that registered the plugin. The vendor panel above will not expose builder endpoints.

enableAppForms is intentionally global. Even though the plugin is registered in only one panel, form schema support is available application-wide. This allows public-facing form submissions (POST /forms/{formKey}/submit) to resolve form definitions regardless of which panel registered the plugin.


Conditional Features

Use ->when() to enable optional plugin features based on config values or authorization checks:

// Config-gated — feature flag in config/tagixo.php
TagixoPrimixPlugin::make()
    ->when(config('tagixo.media_gallery_enabled'), fn ($plugin) => $plugin->withMediaGallery()),

// Permission-gated — only enable media gallery if the current user can manage media
TagixoPrimixPlugin::make()
    ->when(
        fn () => auth()->user()?->can('manage media'),
        fn ($plugin) => $plugin->withMediaGallery(),
    ),

Timing caveat: The closure passed as the first argument to ->when() runs during panel boot, not during the request. At that point the auth guard may not yet have resolved the current user. For permission-based gating, prefer config flags or defer the check to a middleware/policy rather than reading auth()->user() inside a panel boot closure.


Custom Layout Override

To override the Tagixo admin layout Blade view, publish the vendor views:

php artisan vendor:publish --tag=tagixo-primix-views

This copies views to resources/views/vendor/tagixo-primix/. Edit the layout file there.

What not to remove from the published layout:

  • @stack('tagixo-scripts') — required for builder JS assets
  • @livewireStyles / @livewireScripts — required for Primix/Livewire components
  • The Primix asset @yield directives — removing these breaks panel navigation

CSS-only overrides (recommended alternative): If you only need to restyle the admin chrome (fonts, colours, spacing), use a Primix render hook instead of publishing the layout:

// In your AppServiceProvider::boot()
use Primix\Primix\Facades\Primix;
use Primix\Primix\View\PrimixRenderHook;

Primix::registerRenderHook(
    PrimixRenderHook::HEAD_END,
    fn () => '<link rel="stylesheet" href="' . asset('css/admin-overrides.css') . '">',
);

This is safer than a full layout override and will not break when the plugin updates its layout.


Full AdminPanelProvider Example

Complete provider with every documented option shown. Commented-out alternatives are included for reference.

<?php

namespace App\Providers;

use Ccast\TagixoPrimix\TagixoPrimixPlugin;
use Primix\Primix\PrimixPanelProvider;
use Filament\Panel;

class AdminPanelProvider extends PrimixPanelProvider
{
    public function panel(Panel $panel): Panel
    {
        return parent::panel($panel)
            ->id('admin')
            ->path('admin')

            // Authentication
            ->login()
            // ->authMiddleware([Authenticate::class])

            // Navigation
            ->brandName('Tagixo')
            // ->brandLogo(asset('images/logo.svg'))
            // ->favicon(asset('images/favicon.png'))

            // Colors (Primix / Tailwind CSS variables)
            // ->colors(['primary' => Color::Indigo])

            // Plugin registration
            ->plugins([
                TagixoPrimixPlugin::make()

                    // Optional: media gallery resource + picker field
                    ->withMediaGallery(),

                    // Optional: conditional feature based on config
                    // ->when(config('tagixo.media_gallery_enabled'), fn ($p) => $p->withMediaGallery()),
            ]);
    }
}

Asset Publication

Builder JS/CSS assets live in public/vendor/tagixo/ and are served directly by the web server. They are not processed by Vite.

Publish (or re-publish) assets after updating the ccast/tagixo package:

php artisan vendor:publish --tag=tagixo-assets --force

The --force flag is required on subsequent publishes — without it, existing files are not overwritten.

Commit the published assets. The production Docker image is built with composer install --no-scripts, which skips post-install-cmd hooks. Assets must already be present in the repository so the Dockerfile COPY step picks them up. If you skip committing assets, the production build will serve stale JS/CSS.

Vite is not involved in serving these assets. Running npm run build does not update public/vendor/tagixo/. Asset updates come exclusively from vendor:publish.