Installation Guide
This guide covers installing medialight/textstem-laravel into a new or existing Laravel application.
Requirements
PHP and Laravel
| Requirement | Version |
|---|---|
| PHP | >= 8.3 |
| Laravel | 10, 11, 12, or 13 |
| Livewire | ^3.2 |
| Database | MySQL 8+ or PostgreSQL 13+ recommended |
PHP Extensions
The following PHP extensions must be enabled:
| Extension | Used for |
|---|---|
gd or imagick |
Image processing via Glide |
pdo |
Database access |
fileinfo |
File type detection on upload |
zip |
Export commands |
System Packages
| Package | Used for |
|---|---|
| Ghostscript | PDF-to-image conversion (gravitymedia/ghostscript) |
| Node.js + npm (or Bun) | Compiling Jetstream frontend assets |
Install Ghostscript via your OS package manager:
# Debian / Ubuntu
apt-get install ghostscript
# macOS (Homebrew)
brew install ghostscript
Step 1 -- Add the Repository
The package is hosted on a private GitHub repository. Add it to the repositories array in your host app's composer.json before requiring it:
"repositories": [
{
"type": "vcs",
"url": "git@github.com:MeccaMedialight/textstem-laravel.git"
}
]
Ensure your machine or CI environment has SSH access to that repository via a deploy key or personal access token.
Step 2 -- Install via Composer
composer require medialight/textstem-laravel
Laravel's package auto-discovery registers all service providers and facade aliases automatically. No manual entry in config/app.php is needed.
Step 3 -- Run the Post-Install Wizard
php artisan textstem:post-install
The wizard prompts for an installation type and then publishes all required files:
| Option | What it does |
|---|---|
| Vanilla - no Jetstream | Skips Jetstream installation |
| Jetstream with Livewire | Runs jetstream:install livewire |
| Jetstream with Inertia | Runs jetstream:install inertia |
After the stack choice is confirmed, the wizard publishes:
config/textstemapp.php,config/openai.php,config/chunk-upload.php(tag:textstem-config)- Wrangler views to
resources/views/wrangler/(tag:textstem-components) - Auth views to
resources/views/auth/(tag:textstem-components) - JS/CSS assets to
public/vendor/medialight/textstem/including TinyMCE (tag:public) - Jetstream views (tag:
jetstream-views)
Build Frontend Assets (Jetstream installs only)
If you chose a Jetstream stack, compile the frontend before continuing:
npm install && npm run build
# or, if you use Bun:
bun install && bun run build
Step 4 -- Configure Sanctum
The API uses Laravel Sanctum for token authentication. Jetstream installs Sanctum automatically. If you chose the Vanilla option, publish the Sanctum config and migrations manually:
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
If you need cookie-based (stateful) API authentication, add your app's domain to the stateful array in config/sanctum.php:
'stateful' => [
'localhost',
'your-app.test',
env('APP_URL'),
],
Token-based (Bearer) API requests work without this change.
Step 5 -- Configure Environment
Ensure your .env contains a working database connection before running migrations:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your_database
DB_USERNAME=your_user
DB_PASSWORD=your_password
Then add the package-specific variables below. All have defaults in config/textstemapp.php; only set what your app needs to change.
Core
# Email address(es) that receive contact form notifications (comma-separated)
CONTACT_NOTIFICATIONS_EMAIL=you@example.com
# Set to true to redirect all outbound mail to CONTACT_NOTIFICATIONS_EMAIL instead of real recipients
EMAIL_TEST_MODE=false
# Items per page in admin list views
PAGINATION_ITEMS_PER_PAGE=10
# Storage disk for uploaded assets (matches a key in config/filesystems.php)
ASSET_DISK=public
Page Cache
WRANGLER_CACHE_ENABLE=false
WRANGLER_CACHE_TTL=3600
# HTTP cache-control header value: public or private
WRANGLER_CACHE_MODE=private
WRANGLER_CACHE_WITHQUERY=false
TinyMCE
# Leave blank to use the locally published TinyMCE build (no account required)
TINYMCE_API_KEY=
OpenAI / AI Features
OPENAI_API_KEY=sk-...
OPENAI_ENABLED=true
OPENAI_USE_QUEUE=true
OPENAI_MODEL=gpt-4o-mini
Google Cloud Vision
Image comparison and analysis features use Google Cloud Vision. Provide a service account credentials file:
GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
If you do not use image AI features, set textstemapp.images.comparison_driver to fingerprint in config/textstemapp.php to skip Cloud Vision entirely.
Accessibility Analysis
ACCESSIBILITY_ENABLED=true
ACCESSIBILITY_USE_QUEUE=true
ACCESSIBILITY_WCAG_LEVEL=AA # A, AA, or AAA
ACCESSIBILITY_CHECK_ON_SAVE=false
ACCESSIBILITY_STORE_REPORTS=true
ACCESSIBILITY_REPORT_RETENTION_DAYS=30
SEO Analysis
SEO_ENABLED=true
SEO_USE_QUEUE=true
SEO_USE_AI=true
SEO_CHECK_ON_SAVE=false
SEO_STORE_REPORTS=true
SEO_REPORT_RETENTION_DAYS=30
File Uploads
TEXTSTEM_ALLOWED_EXTENSIONS=jpg,jpeg,png,gif,svg,webp,pdf,doc,docx,xls,xlsx,ppt,pptx,txt,zip,mp3,wav,mp4,mov,avi,wmv
TEXTSTEM_MAX_FILE_SIZE=10240 # kilobytes; default is 10 MB
TEXTSTEM_STORAGE_PATH=public/uploads
TEXTSTEM_UPLOADS_USE_QUEUE=false
TEXTSTEM_UPLOADS_ENABLE_FINGERPRINT=true
TEXTSTEM_UPLOADS_GENERATE_THUMBNAILS=true
TEXTSTEM_UPLOADS_EXTRACT_METADATA=true
Redis (optional)
TEXTSTEM_USE_REDIS=false
Set to true if your environment has Redis available. The package will use it for caching and queues.
Admin UI
DARK_MODE=false
Step 6 -- Run Migrations
Installing into an existing app? If your app already has a
userstable, the package migration2014_10_12_000000_create_users_table.phpwill conflict and fail. Skip it by inserting the migration filename into themigrationstable manually, then runphp artisan migrate. Thetextstem:migrations:synccommand can do this in bulk -- see the artisan commands reference.
Run all outstanding migrations:
php artisan migrate
This creates the following tables (in addition to Laravel defaults):
| Table | Description |
|---|---|
users |
Extended user model with roles and Jetstream traits |
options |
Global site options |
events |
Event records |
previews |
Page preview snapshots |
categories |
Shared category model for pages, posts, and assets |
wrangler_pages |
Page-builder pages |
wrangler_components |
Page components |
assets |
Media asset library |
posts |
Blog/news posts |
post_collections |
Post groupings |
redirects |
URL redirect rules |
tags / taggables |
Spatie tag tables |
messages |
In-app messages |
roles / permissions |
Spatie permission tables |
personal_access_tokens |
Sanctum API tokens |
prompts |
AI prompt library |
seo_reports |
Stored SEO analysis results |
accessibility_reports |
Stored accessibility analysis results |
orchestration_* |
AI orchestration state tables |
Step 7 -- Set Up Asset Storage
Run Laravel's storage link command so uploaded files in the public disk are accessible via the web:
php artisan storage:link
For S3 storage, configure the s3 disk in config/filesystems.php and set ASSET_DISK=s3 in .env.
Step 8 -- Set Up Queues
AI generation, accessibility and SEO analysis, and asset processing jobs are dispatched to a queue. Configure a real queue driver for production:
QUEUE_CONNECTION=redis # or database, sqs, etc.
Start a worker:
php artisan queue:work --queue=default --tries=3
For production, manage the worker process with Supervisor or Laravel Horizon.
Step 9 -- Register the Scheduler
The package registers scheduled commands automatically via the service provider. Add the scheduler call to your server crontab so they execute:
* * * * * cd /path-to-your-app && php artisan schedule:run >> /dev/null 2>&1
Commands the package schedules:
| Command | Schedule | Description |
|---|---|---|
textstem:prune-reports |
Daily at 03:00 | Removes old SEO and accessibility reports |
textstem:health-check |
Daily at 01:00 | Checks application health |
cache:manage clear --warm-up |
Mondays at 01:30 | Clears and re-warms the cache |
Step 10 -- Create a Super Admin User
Register a user through your app's registration flow, then create the super-admin role (if it does not already exist) and assign it:
use Medialight\Textstem\Models\User;
use Spatie\Permission\Models\Role;
$role = Role::firstOrCreate(['name' => 'super-admin']);
$user = User::find(1);
$user->assignRole($role);
This can be run in Tinker (php artisan tinker) or placed in a seeder.
Super-admin users bypass all gate checks and have unrestricted access to the admin panel.
Step 11 -- Seed Sample Data (optional)
php artisan textstem:dbseed
Creates sample pages, posts, categories, and assets to give the admin panel something to work with immediately.
Verification
After completing the steps above, confirm the following:
public/vendor/medialight/textstem/exists and contains JS/CSS filesresources/views/wrangler/exists withcomponents/,pages/,posts/, andtemplate-commands/subdirectoriesconfig/textstemapp.phpexists in the host appphp artisan migrate:statusshows all package migrations asRan- The admin panel loads at
/textstemwithout errors /textstem/dashboardis accessible after logging in as the super-admin user
Updating the Package
composer update medialight/textstem-laravel
php artisan migrate
php artisan textstem:refresh-assets
Review config/textstem.php in the package against your published config/textstemapp.php after each update and merge in any new keys.
Notes
User Model
The service provider overrides auth.providers.users.model at runtime to point to Medialight\Textstem\Models\User. This model extends the standard Laravel user and adds Spatie role/permission support and Jetstream traits.
If your app needs a custom user model, extend the package model rather than Illuminate\Foundation\Auth\User:
// app/Models/User.php
class User extends \Medialight\Textstem\Models\User
{
// your customisations
}
Then re-point the auth config in your AppServiceProvider:
public function boot(): void
{
config(['auth.providers.users.model' => \App\Models\User::class]);
}
Localization Middleware
The Localization middleware is appended to the web group automatically by the service provider. It reads a locale from the session and calls app()->setLocale(). No manual registration is needed.
Re-publishing Assets
If you update the package or suspect published files are out of date, re-run:
php artisan textstem:refresh-assets
This clears public/vendor/medialight/textstem/, re-publishes the JS/CSS and TinyMCE build, and ensures the required Wrangler view directories exist.