Solo for Laravel

repository·main·Indexed 23 days ago

https://github.com/soloterm/solo

A TUI (Terminal User Interface) development tool for Laravel that allows running multiple commands—such as Vite, logs, and queues—simultaneously within a single, tabbed terminal interface. It features a custom Dump Server to intercept dump() calls, a universal MakeCommand for artisan generators, and support for custom themes, keybindings, and command classes. Requires ext-pcntl and is not compatible with Windows.

Tokens
12.4K
Snippets
43
Records
79
Agent score
75%

What's inside Solo for Laravel

  1. How Solo's architecture works

    main

    Solo is a TUI (Terminal User Interface) built for Laravel that operates using three primary components:

    1. Dashboard: The central TUI controller that manages command tabs, keyboard input, and coordinates the rendering process.
    2. Commands: Individual processes running in separate tabs. Each command manages its own subprocess lifecycle, output buffering, and scroll state.
    3. Renderer: Responsible for frame generation, including the tab bar, command output, hotkey bar, and layout borders.

    The data flow follows this path: SubprocessstdoutCommand::collectIncrementalOutput()Screen::write()Screen bufferScreen::output()RendererTerminal.

  2. Configure lazy commands

    main

    To prevent certain commands from starting automatically when Solo launches, mark them as lazy(). These commands will only start when you manually trigger them using the s key.

    Example configuration:

    'Queue' => Command::from('php artisan queue:work')->lazy(),
  3. Understand Solo's rendering and performance model

    main

    Solo is optimized for low CPU usage and high responsiveness through several mechanisms:

    • Event Loop: Runs at approximately 40 FPS (25ms intervals), polling input, ticking commands, and rendering.
    • Differential Rendering: Instead of redrawing the entire screen, Solo tracks sequence numbers and only outputs lines that have changed. This reduces terminal I/O by approximately 99.5%.
    • Non-blocking I/O: Subprocess output is collected incrementally using non-blocking streams to prevent the TUI from freezing while waiting for process output.
    • Virtual Terminal Buffer: Uses the soloterm/screen package to interpret ANSI escape sequences and manage a scrollback buffer before sending data to the terminal.
  4. Configure commands for Laravel Sail

    main

    If you are using Laravel Sail, you must update your config/solo.php to use the Sail binary for your commands so they execute within the Sail container environment.

    'commands' => [
        'Queue' => 'vendor/bin/sail artisan queue:work --ansi',
        'Vite' => 'vendor/bin/sail npm run dev',
    ],
  5. Use interactive mode for command input

    main

    If a running command requires user input (e.g., a confirmation prompt), enter Interactive Mode:

    1. Press i to enter interactive mode.
    2. Type your input.
    3. Press Ctrl+X to exit interactive mode and return to standard navigation.
  6. Default Keybindings in Solo

    main

    Solo is keyboard-driven. Use the following default keybindings for navigation, command control, and interactive mode:

    • / : Switch between tabs
    • / : Scroll output up/down
    • Shift+↑ / Shift+↓: Page up/down
    • Home: Jump to top
    • End: Jump to bottom
    • g: Open tab picker (jump to any tab)

    Command Control

    • s: Start/Stop current command
    • r: Restart current command
    • c: Clear output
    • p: Pause (stop auto-scrolling)
    • f: Follow (resume auto-scrolling)

    Interactive Mode

    Use interactive mode to forward keystrokes to the underlying command (e.g., for php artisan tinker).

    • i: Enter interactive mode
    • Ctrl+X: Exit interactive mode

    Global

    • q: Quit Solo
    • Ctrl+C: Quit Solo
  7. Create Custom Keybindings

    main

    To create a custom keybinding set, implement the SoloTerm\Solo\Contracts\HotkeyProvider interface. You can extend the DefaultHotkeys to modify specific keys while keeping the rest of the defaults.

    1. Create a class implementing HotkeyProvider.
    2. Implement keys() to return the array of Hotkey objects.
    3. Implement keymap() to define the mapping (e.g., using remap() on existing hotkeys).
    4. Register the class in config/solo.php under the keybindings array and set 'keybinding' => 'custom'.
    namespace App\
    Solo\\Hotkeys;
    
    use Laravel\\Prompts\\Key;
    use SoloTerm\\Solo\\Contracts\\HotkeyProvider;
    use SoloTerm\\Solo\\Hotkeys\\DefaultHotkeys;
    use SoloTerm\\Solo\\Hotkeys\\Hotkey;
    use SoloTerm\\Solo\\Hotkeys\\KeyHandler;
    
    class CustomHotkeys implements HotkeyProvider
    {
        public static function keys(): array
        {
            return array_values(static::keymap());
        }
    
        public static function keymap(): array
        {
            // Start with default keys
            $map = DefaultHotkeys::keymap();
    
            // Modify specific keys
            $map['quit']->remap('x');  // Use 'x' instead of 'q' to quit
    
            return $map;
        }
    }
  8. Create Custom Command Classes

    main

    For complex logic, you can create a custom class by extending SoloTerm\Solo\Commands\Command. This allows you to define custom names, commands, autostart behavior, and even custom hotkeys via the hotkeys() method.

    namespace App\Solo\Commands;
    
    use SoloTerm\Solo\Commands\Command;
    
    class MyCustomCommand extends Command
    {
        public function __construct()
        {
            parent::__construct(
                name: 'Custom',
                command: 'my-command --option',
                autostart: true,
            );
        }
    
        public function boot(): void
        {
            // Called when the command is initialized
        }
    
        public function hotkeys(): array
        {
            // Add custom hotkeys for this command
            return [
                // 'key' => Hotkey::make('k', $handler)->label('Label'),
            ];
        }
    }

    Registration:

    'commands' => [
        'Custom' => new \App\Solo\Commands\MyCustomCommand,
    ],
  9. Tips for better command output

    main

    To ensure commands look correct in Solo, consider these tips:

    • Force ANSI Colors: Many tools disable colors when they detect they aren't in a TTY. Use flags like --colors=always or --ansi to force color output.
    • Laravel Sail: If using Sail, use the vendor/bin/sail binary in your command definition.
    • Long-running processes: Use ->lazy() for servers or queue workers to prevent them from cluttering your startup.
    // Force colors
    'Tests' => Command::from('php artisan test --colors=always')->lazy(),
    'Pint' => Command::from('./vendor/bin/pint --ansi')->lazy(),
    
    // Laravel Sail
    'Queue' => 'vendor/bin/sail artisan queue:work --ansi',
    
    // Lazy long-running commands
    'Queue' => Command::from('php artisan queue:work')->lazy(),
    'Horizon' => Command::from('php artisan horizon')->lazy(),
  10. Define commands in Solo

    main

    Commands are defined in the commands array within config/solo.php. Each array key represents the name that will be displayed on the tab in the Solo interface. You can define commands using simple strings or the Command class for advanced configuration.

    'commands' => [
        'About' => 'php artisan solo:about',
        'Logs' => 'tail -f -n 100 ' . storage_path('logs/laravel.log'),
        'Vite' => 'npm run dev',
        'Queue' => Command::from('php artisan queue:work')->lazy(),
    ],
  11. Switch Solo themes

    main

    You can switch between the built-in dark and light themes by updating your configuration file or using an environment variable.

    To set the theme in config/solo.php:

    'theme' => 'dark',  // or 'light'

    To set the theme via an environment variable:

    SOLO_THEME=light
    // config/solo.php
    'theme' => 'dark',  // or 'light'