Spotlight
A powerful command palette and search interface with three modes: Search, Commands, and AI.
Quick Start
The Spotlight is automatically included with
@neuraKit.
Press ⌘K to open.
Architecture
Spotlight uses three types of providers, each in its own folder:
app/Spotlight/Commands/
Actions shown in Command mode (toggle theme, navigate, open modal...)
app/Spotlight/Search/
Search providers for Search mode (users, pages, blog posts...)
app/Spotlight/Ai/
AI providers for AI mode with streaming support (OpenAI, Anthropic, custom...)
Artisan Generator
Use
make:spotlight to scaffold any Spotlight component:
# Action command (default) → app/Spotlight/Commands/
php artisan make:spotlight ToggleDarkMode
# Navigation command → app/Spotlight/Commands/
php artisan make:spotlight GoToSettings --type=navigation
# Search provider → app/Spotlight/Search/
php artisan make:spotlight UsersSearch --type=search
# AI provider → app/Spotlight/Ai/
php artisan make:spotlight OpenAiProvider --type=ai
Stubs are located in
neura-kit/stubs/Spotlight/ — command.stub, search-provider.stub, ai-provider.stub.
Keyboard Shortcuts
Open Search
⌘K
Open Commands
⌘P
Open AI
⌘I
Close
Esc
Navigate
↑
↓
Select
Enter
Switch Mode
Tab
Commands
Commands are actions shown in Command mode. They extend
SpotlightCommand.
Simple Command
<?php
namespace App\Spotlight\Commands;
use Neura\Kit\Support\Spotlight\SpotlightCommand;
use Neura\Kit\Support\Spotlight\Enums\SpotlightGroup;
class ToggleDarkModeCommand extends SpotlightCommand
{
protected string $name = 'Toggle Dark Mode';
protected string $description = 'Switch between light and dark theme';
protected ?string $icon = 'moon';
protected ?string $shortcut = '⌘⇧D';
protected SpotlightGroup $group = SpotlightGroup::Settings;
protected int $priority = 80;
protected array $keywords = ['theme', 'dark', 'light'];
public function execute(array $params = []): mixed
{
return $this->emit('theme:toggle');
}
}
Available Helpers
$this->goTo('/url')
Navigate to a URL
$this->emit('event')
Emit a browser event
$this->wire('method')
Call a Livewire method
$this->copy('text')
Copy text to clipboard
$this->modal('name')
Open a modal
Searchable Navigation Command
<?php
namespace App\Spotlight\Commands;
use Illuminate\Support\Collection;
use Neura\Kit\Support\Spotlight\SpotlightCommand;
use Neura\Kit\Support\Spotlight\Enums\SpotlightGroup;
class NavigationCommand extends SpotlightCommand
{
protected string $name = 'Navigate to Pages';
protected ?string $icon = 'arrow-right-circle';
protected SpotlightGroup $group = SpotlightGroup::Navigation;
protected array $pages = [
['name' => 'Home', 'url' => '/', 'icon' => 'home'],
['name' => 'Documentation', 'url' => '/docs', 'icon' => 'document-text'],
];
public function execute(array $params = []): mixed
{
return $this->goTo('/');
}
public function search(string $query): Collection
{
return $this->searchLinks($this->pages, $query);
}
}
Search Providers
Search providers power the Search mode. They extend
SpotlightSearchProvider and return a Collection of results.
<?php
namespace App\Spotlight\Search;
use Illuminate\Support\Collection;
use Neura\Kit\Support\Spotlight\SpotlightSearchProvider;
use Neura\Kit\Support\Spotlight\SpotlightResult;
class UsersSearchProvider extends SpotlightSearchProvider
{
protected string $id = 'users';
protected string $name = 'Users';
protected int $priority = 80;
protected int $minQueryLength = 2;
protected array $keywords = [];
public function search(string $query): Collection
{
return User::where('name', 'like', "%{$query}%")
->limit(5)
->get()
->map(fn ($user) => SpotlightResult::url(
id: "user-{$user->id}",
title: $user->name,
url: "/users/{$user->id}",
description: $user->email,
icon: 'user'
));
}
}
AI Providers
AI providers power the AI mode. They extend
SpotlightAiProvider.
The $stream() callback uses Livewire wire:stream for real-time streaming. After loading, the response is formatted as Markdown.
Simple AI Provider
<?php
namespace App\Spotlight\Ai;
use Neura\Kit\Support\Spotlight\SpotlightAiProvider;
class MyAiProvider extends SpotlightAiProvider
{
protected int $priority = 10;
protected array $keywords = ['help', 'how to'];
public function handle(string $query, callable $stream): ?string
{
$stream("**Hello!** You asked: {$query}\n\n");
$stream("Here is my answer...");
return null;
}
}
OpenAI Streaming
<?php
namespace App\Spotlight\Ai;
use Neura\Kit\Support\Spotlight\SpotlightAiProvider;
use OpenAI\Laravel\Facades\OpenAI;
class OpenAiProvider extends SpotlightAiProvider
{
protected int $priority = 100;
public function handle(string $query, callable $stream): ?string
{
$result = OpenAI::chat()->createStreamed([
'model' => 'gpt-4',
'messages' => [
['role' => 'user', 'content' => $query],
],
]);
foreach ($result as $response) {
$content = $response->choices[0]->delta->content ?? '';
if ($content) {
$stream($content);
}
}
return null;
}
}
Proxy AI Provider
Route queries to specialized AI providers based on keywords:
<?php
namespace App\Spotlight\Ai;
use Neura\Kit\Support\Spotlight\SpotlightAiProvider;
class ProxyAiProvider extends SpotlightAiProvider
{
protected int $priority = 200; // Highest priority
protected array $providers = [
'code' => CodeHelperAiProvider::class,
'components' => ComponentsAiProvider::class,
'general' => GeneralAiProvider::class,
];
protected array $routes = [
'code' => ['code', 'function', 'class', 'implement'],
'components' => ['button', 'modal', 'input', 'component'],
];
public function handle(string $query, callable $stream): ?string
{
$type = $this->determineProvider($query);
return app($this->providers[$type])->handle($query, $stream);
}
protected function determineProvider(string $query): string
{
$q = strtolower($query);
foreach ($this->routes as $type => $keywords) {
foreach ($keywords as $kw) {
if (str_contains($q, $kw)) return $type;
}
}
return 'general';
}
public function canHandle(string $query): bool
{
return true;
}
}
During loading:
wire:stream pushes raw text in real-time.
After loading: x-html renders the formatted Markdown response.
Custom AI View
Override the default AI response view with your own Blade template:
// In SpotlightServiceProvider.php
SpotlightRegistry::configure(new SpotlightConfig(
aiView: 'components.spotlight.my-ai-view',
));
Custom View Example
Create
resources/views/components/spotlight/my-ai-view.blade.php:
<div x-show="!aiResponse && !isLoading" class="py-12 text-center">
<p>Ask me anything!</p>
</div>
<div x-show="isLoading && !aiResponse" class="p-6">
<x-neura::spinner />
</div>
<div x-show="aiResponse" class="p-6">
<div x-show="isLoading" wire:stream="spotlightAiStream"
class="prose prose-sm dark:prose-invert whitespace-pre-wrap">
</div>
<div x-show="!isLoading"
x-html="formatAiResponse(aiResponse)"
class="prose prose-sm dark:prose-invert">
</div>
</div>
Your custom view must include
wire:stream="spotlightAiStream" for real-time streaming and x-html="formatAiResponse(aiResponse)" for formatted display.
Service Provider
Register all Spotlight components in a dedicated service provider:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Neura\Kit\Support\Spotlight\SpotlightConfig;
use Neura\Kit\Support\Spotlight\SpotlightRegistry;
use App\Spotlight\Commands\ToggleDarkModeCommand;
use App\Spotlight\Commands\NavigationCommand;
use App\Spotlight\Search\UsersSearchProvider;
use App\Spotlight\Ai\ProxyAiProvider;
class SpotlightServiceProvider extends ServiceProvider
{
public function boot(): void
{
// Configuration (optional)
SpotlightRegistry::configure(new SpotlightConfig(
maxResults: 20,
showModes: true,
// aiView: 'components.spotlight.my-ai-view',
));
// Commands → shown in Command mode (⌘P)
SpotlightRegistry::registerMany([
ToggleDarkModeCommand::class,
NavigationCommand::class,
]);
// Search providers → power Search mode (⌘K)
SpotlightRegistry::registerSearchProviderClasses([
UsersSearchProvider::class,
]);
// AI providers → power AI mode (⌘I)
SpotlightRegistry::registerAiProvider(ProxyAiProvider::class);
}
}
Add
SpotlightServiceProvider::class to bootstrap/providers.php.
JavaScript API
// Open with mode
NeuraKitSpotlight.open()
NeuraKitSpotlight.open({ mode: 'command' })
NeuraKitSpotlight.open({ mode: 'ai', placeholder: 'Ask me...' })
NeuraKitSpotlight.open({ query: 'button' })
// Close / Toggle
NeuraKitSpotlight.close()
NeuraKitSpotlight.toggle({ mode: 'search' })
// Mode
NeuraKitSpotlight.setMode('ai')
NeuraKitSpotlight.getModes() // ['search', 'command', 'ai']
// State
NeuraKitSpotlight.isOpen()
NeuraKitSpotlight.setLoading(true)
// Results
NeuraKitSpotlight.setResults([
{ id: '1', title: 'Result', url: '/page' }
])
// Execute a command
NeuraKitSpotlight.execute('toggle-dark-mode')
// Stream AI response
NeuraKitSpotlight.stream('Hello!', true) // append
NeuraKitSpotlight.stream('New response', false) // replace
Result Types
use Neura\Kit\Support\Spotlight\SpotlightResult;
// Navigate to URL
SpotlightResult::url(
id: 'home', title: 'Home', url: '/',
description: 'Go home', icon: 'home'
);
// Dispatch event
SpotlightResult::dispatch(
id: 'toggle-theme', title: 'Toggle Theme',
event: 'theme:toggle', icon: 'moon'
);
// Call Livewire method
SpotlightResult::wire(
id: 'save', title: 'Save',
method: 'save', params: ['draft' => false], icon: 'check'
);
// Copy to clipboard
SpotlightResult::copy(
id: 'copy-link', title: 'Copy Link',
text: 'https://example.com', icon: 'clipboard'
);
// Open modal
SpotlightResult::modal(
id: 'settings', title: 'Settings',
modalName: 'settings-modal', icon: 'cog'
);
Configuration
SpotlightRegistry::configure(new SpotlightConfig(
defaultMode: SpotlightMode::Search,
debounceMs: 150,
maxResults: 20,
showModes: true,
showFooter: true,
panelSize: 'lg', // 'sm', 'md', 'lg', 'xl'
panelPosition: 'top', // 'top', 'center'
aiView: null, // Custom Blade view for AI response
aiPlaceholder: null, // Custom AI placeholder text
// Or use placeholders array for any mode:
placeholders: [
'search' => 'Search docs...',
'command' => 'Run a command...',
'ai' => 'Ask anything...', // Overrides aiPlaceholder
],
));