ozmos / starboard
A command palette for Laravel
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/ozmos/starboard
Requires
- php: ^8.4
- illuminate/contracts: ^11.0||^12.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- carthage-software/mago: ^1.0.0-beta.34
- laravel/framework: 12.37.0
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.0.0||^9.0.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- spatie/laravel-ray: ^1.35
README
Starboard is a command palette for Laravel applications. It provides a powerful, extensible command interface that allows you to quickly access and interact with your application's functionality. It is intended for use within the development environment only, but you can use it in production if you so desire.
Table of Contents
- Requirements
- Installation
- Usage
- Built in commands
- Custom Commands
- View Types Reference
- Advanced Usage
- Configuring
- Keyboard Shortcuts
- Troubleshooting
- Contributing
- Support
- License
Installation
Require the package through composer
composer require ozmos/starboard
Publish the javascript assets to ./public/vendor/starboard
php artisan vendor:publish --tag="starboard-assets"
Usage
Enable Starboard and register commands in a service provider
use Illuminate\Support\ServiceProvider; use Ozmos\Starboard\Facades\Starboard; class AppServiceProvider extends ServiceProvider { public function boot(): void { Starboard::enable(app()->isLocal()); Starboard::register([ \Ozmos\Starboard\Commands\ImpersonateUser::make(), \Ozmos\Starboard\Commands\TelescopeRequests::make(), ]); } }
Render starboard in your application using the @starboard blade directive, most likely in your root layout
<html> <body> @starboard </body> </html>
Built in commands
ImpersonateUser
Search your User model and log in as user.
Starboard::register([ \Ozmos\Starboard\Commands\ImpersonateUser::make() ]);
You can optionally configure this command further to suit your application
Starboard::register([ \Ozmos\Starboard\Commands\ImpersonateUser::make() // title of the command (appears in initial palette list) ->commandTitle('Impersonate User') // what model to query ->model(\App\Models\User::class) // searchable table columns ->columns(['id', 'name', 'email']) // customise how the users appear in the list ->itemBuilder(function (ListItemBuilder $builder, $user) { return $builder ->title($user->name) ->subtitle($user->email) ->id($user->getKey()); }) // search filter placeholder ->placeholder('Search for a user') // what happens when the user is selected in the list ->onSelect(function (string $id) { $user = $this->model::find($id); Auth::login($user); return CommandResponse::make()->reload(); }) ]);
TailLogs
Returns the last n lines of a log file
Starboard::register([ \Ozmos\Starboard\Commands\TailLogs::make() ]);
You can optionally configure the log file and number of lines returned
Starboard::register([ \Ozmos\Starboard\Commands\TailLogs::make() ->logFile(storage_path('logs/laravel.log')) ->lines(500) ]);
Telescope Requests
View the most recent requests from your application. Requires telescope to be installed/enabled
Starboard::register([ \Ozmos\Starboard\Commands\TelescopeRequests::make(), ]);
Telescope Mail
View the most recent mail sent from your application. Requires telescope to be installed/enabled
Starboard::register([ \Ozmos\Starboard\Commands\TelescopeMail::make(), ]);
Telescope Jobs
View the most recent jobs from your application. Requires telescope to be installed/enabled
Starboard::register([ \Ozmos\Starboard\Commands\TelescopeJobs::make(), ]);
Custom Commands
All commands extend from the Ozmos\Starboard\Commands\BaseCommand class. You can either extend this base class directly, or use one of the several command builders offered to speed up the process.
Anatomy of a command
All commands extend BaseCommand and must implement:
id(): string- A unique identifier for the commandtitle(): string- The display name shown in the command paletterender(CommandContext $context)- Returns a view that defines what the command displays
The render method must return a view provided by the Ozmos\Starboard\Commands\Views\CommandView class. There are two main view types:
List Views
List views display a searchable list of items. Use CommandView::list() to create a list view:
return CommandView::list() ->render(function (ListBuilder $builder) { // Return a collection of ListItemBuilder instances return collect($items)->map(function ($item) use ($builder) { return $builder ->item() ->title($item->name) ->subtitle($item->description) ->id($item->id); }); }) ->rerunsOnInputChange() // Re-run when user types ->placeholder('Search items...') ->onSelect(function (string $id) { // Handle item selection return CommandResponse::make()->push($detailCommand); }) ->action('refresh', function (string $listItemId) { // Custom action for list items return CommandResponse::make()->toast('Refreshed!'); });
List views support:
- Async filtering: Use
rerunsOnInputChange()to filter results as the user types - Pagination: Return a paginated result from your query (Laravel's paginator is supported)
- Actions: Add custom actions that appear as buttons for each list item
- Placeholder text: Customize the search input placeholder
Detail Views
Detail views display detailed information about a single item. Use CommandView::detail() to create a detail view:
return CommandView::detail() ->render(function (DetailBuilder $builder) { return $builder ->text('Label', 'Value') // Plain text field ->preformatted('Code', $code) // Preformatted text (monospace) ->markdown('Description', $markdown) // Markdown content (rendered as HTML) ->html('Content', $html, iframe: false) // Raw HTML content ->action('refresh', function () { return CommandResponse::make() ->replace($this) ->toast('Refreshed!'); }); });
Detail views support:
- Text blocks: Simple label-value pairs
- Preformatted blocks: Monospace text (great for code, JSON, etc.)
- Markdown blocks: Rendered markdown content
- HTML blocks: Raw HTML (with optional iframe rendering)
- Actions: Add custom action buttons
Model searching commands
If your command operates around searching a list of models you can use the ModelLookup command builder
Starboard::register([ \Ozmos\Starboard\Commands\ModelLookup::make() // title of the command (appears in initial palette list) ->commandTitle('Team Lookup') // id of the command (must be unique across all registered commands) ->commandId('team-lookup') // what model to query ->model(\App\Models\User::class) // searchable table columns ->columns(['id', 'name', 'email']) // customise how the users appear in the list ->itemBuilder(function (ListItemBuilder $builder, $user) { return $builder ->title($user->name) ->subtitle($user->email) ->id($user->getKey()); }) // search filter placeholder ->placeholder('Search for a user') // what happens when the user is selected in the list ->onSelect(function (string $id) { $user = $this->model::find($id); Auth::login($user); return CommandResponse::make()->reload(); }) ]);
Building a command from scratch
Here we will build a complete example that demonstrates:
- Creating a custom command that returns a list of items
- Creating a child command that shows detail views
- Using CommandResponse to navigate between commands
- Using actions for additional functionality
Example: Language Search Command
First, let's create a command that searches through a list of programming languages:
use Ozmos\Starboard\Commands\BaseCommand; use Ozmos\Starboard\Commands\CommandContext; use Ozmos\Starboard\Commands\CommandResponse; use Ozmos\Starboard\Commands\Views\CommandView; use Ozmos\Starboard\Commands\Views\ListBuilder; class SearchLanguages extends BaseCommand { public function id(): string { return 'search-languages'; } public function title(): string { return 'Search Languages'; } public function render(CommandContext $context) { $languages = ['PHP', 'JavaScript', 'Python', 'Ruby', 'Go', 'Rust']; return CommandView::list() ->render(function (ListBuilder $builder) use ($context, $languages) { return collect($languages) ->when($context->input(), fn($query) => $query->filter( fn($language) => str_contains(strtolower($language), strtolower($context->input())) )) ->map(function ($language) use ($builder) { return $builder ->item() ->title($language) ->id($language); }); }) ->rerunsOnInputChange() ->placeholder('Search for a programming language...') ->onSelect($this->onSelect(...)); } private function onSelect(string $language) { return CommandResponse::make()->push(LanguageDetail::make()->forLanguage($language)); } public function children(): array { return [ LanguageDetail::make()->hidden(), ]; } }
Now, let's create the detail command that shows information about a selected language:
use Ozmos\Starboard\Commands\BaseCommand; use Ozmos\Starboard\Commands\CommandContext; use Ozmos\Starboard\Commands\CommandResponse; use Ozmos\Starboard\Commands\Views\CommandView; use Ozmos\Starboard\Commands\Views\DetailBuilder; class LanguageDetail extends BaseCommand { public string $language; public function forLanguage(string $language): self { $this->language = $language; return $this; } public function id(): string { return 'language-detail'; } public function title(): string { return 'Language Detail'; } public function render(CommandContext $context) { return CommandView::detail() ->render(function (DetailBuilder $builder) { $info = $this->getLanguageInfo($this->language); return $builder ->text('Name', $this->language) ->text('Type', $info['type']) ->text('Year Created', $info['year']) ->preformatted('Description', $info['description']) ->markdown('Features', $info['features']) ->action('view-docs', fn() => CommandResponse::make() ->openUrl($info['docs_url']) ->toast('Opening documentation...')); }); } private function getLanguageInfo(string $language): array { // Your logic to fetch language information return [ 'type' => 'Interpreted', 'year' => '1995', 'description' => 'A popular programming language...', 'features' => '- Feature 1\n- Feature 2', 'docs_url' => "https://example.com/docs/{$language}", ]; } }
Register your custom commands:
Starboard::register([ SearchLanguages::make(), ]);
Understanding Command Responses
When a user interacts with your command (selects an item, clicks an action), you return a CommandResponse that tells Starboard what to do next:
push($command)- Navigate to a new command (adds to navigation stack)replace($command)- Replace the current commandopenUrl($url)- Open a URL in a new tabreload()- Reload the current pagedismiss()- Close the command palettetoast($message)- Show a toast notification (can be chained with other responses)
Understanding Command Context
The CommandContext provides information about the current state:
$context->input()- The current search input from the user$context->page()- The current page number (for pagination)$context->cursor()- The pagination cursor (for cursor-based pagination)
Child Commands
Child commands are commands that are automatically registered when their parent is registered, but are marked as hidden (not discoverable in the root command list). They're typically used for detail views or sub-commands that are only accessible through their parent.
To create a child command, return it from the children() method and mark it as hidden:
public function children(): array { return [ LanguageDetail::make()->hidden(), ]; }
View Types Reference
ListBuilder Methods
render(Closure $callback)- Define how list items are generated. The callback receives aListBuilderand should return a collection ofListItemBuilderinstances or a paginator.item()- Create a new list item builderplaceholder(string $text)- Set the search input placeholderrerunsOnInputChange(bool $async = true)- Enable automatic re-rendering when the user typesonSelect(Closure $callback)- Handle item selection. Callback receives the item ID and should return aCommandResponseaction(string $name, Closure $callback)- Add a custom action button. Callback receives the list item ID
ListItemBuilder Methods
id(string $id)- Set the unique identifier for this itemtitle(string $title)- Set the main title textsubtitle(?string $subtitle)- Set optional subtitle text
DetailBuilder Methods
render(Closure $callback)- Define the detail content. The callback receives aDetailBuildertext(string $label, string $value)- Add a plain text fieldpreformatted(string $label, string $value)- Add preformatted (monospace) textmarkdown(string $label, string $value)- Add markdown content (rendered as HTML)html(string $label, string $value, bool $iframe = false)- Add raw HTML contentaction(string $name, Closure $callback)- Add a custom action button
CommandResponse Methods
push(BaseCommand $command)- Navigate to a new command (adds to navigation history)replace(BaseCommand $command)- Replace the current command (no history)openUrl(string $url)- Open a URL in a new browser tabreload()- Reload the current pagedismiss()- Close the command palettetoast(string $message)- Show a toast notification (can be chained)
CommandContext Methods
input()- Get the current search input valuepage()- Get the current page number (for pagination)cursor()- Get the pagination cursor (for cursor-based pagination)
Advanced Usage
Custom Query Logic
When using ModelLookup, you can override the default query logic:
\Ozmos\Starboard\Commands\ModelLookup::make() ->model(\App\Models\User::class) ->columns(['name', 'email']) ->query(function (CommandContext $context) { // Custom query logic return \App\Models\User::query() ->where('active', true) ->when($context->input(), function ($query) use ($context) { $query->where('name', 'like', '%' . $context->input() . '%'); }) ->orderBy('created_at', 'desc') ->limit(20); })
Pagination
List views support Laravel's pagination. Simply return a paginated result:
return CommandView::list() ->render(function (ListBuilder $builder) use ($context) { return \App\Models\User::query() ->when($context->input(), fn($q) => $q->where('name', 'like', '%' . $context->input() . '%')) ->paginate(perPage: 10, page: $context->page()) ->through(function ($user) use ($builder) { return $builder ->item() ->title($user->name) ->id($user->id); }); });
List Item Actions
Add custom actions to list items that appear as buttons:
return CommandView::list() ->render(function (ListBuilder $builder) { // ... build items }) ->action('edit', function (string $itemId) { return CommandResponse::make() ->openUrl("/users/{$itemId}/edit") ->toast('Opening editor...'); }) ->action('delete', function (string $itemId) { \App\Models\User::find($itemId)->delete(); return CommandResponse::make() ->replace($this) ->toast('User deleted'); });
Making Commands Hidden
Commands that shouldn't appear in the root command list can be marked as hidden:
$command->hidden();
Hidden commands are typically child commands that are only accessible through their parent command.
Configuring
Publish the config to config/starboard.php
php artisan vendor:publish --tag="starboard-config"
The configuration file allows you to customize the routing for Starboard endpoints:
return [ 'routes' => [ 'prefix' => '_starboard', // URL prefix for Starboard routes 'middleware' => ['web'], // Middleware to apply to routes 'name' => 'starboard.', // Route name prefix ], ];
Keyboard Shortcuts
Starboard supports the following keyboard shortcuts:
Cmd+K/Ctrl+K- Open/close the command paletteEsc- Close the command palette or go back↑/↓- Navigate through list itemsEnter- Select the highlighted itemCmd+Enter/Ctrl+Enter- Execute the first action (if available)
Troubleshooting
Commands not appearing
- Ensure Starboard is enabled:
Starboard::enable(true) - Check that commands are registered in a service provider's
boot()method - Verify commands are marked as discoverable (not hidden)
Command ID conflicts
If you see "Command already registered" errors, ensure all command IDs are unique. You can override the id() method to provide a custom identifier.
Assets not loading
Make sure you've published the assets:
php artisan vendor:publish --tag="starboard-assets"
And that the @starboard directive is included in your layout file.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Support
For issues, questions, or feature requests, please visit the GitHub repository.
License
Starboard is open-sourced software licensed under the MIT license.
