Back to Home

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
    ],
));

Try It

Open by Mode

With Options