Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Tailcli Laravel Package

nunomaduro/tailcli

A Laravel package from Nuno Maduro that adds an elegant Artisan tail command for streaming application logs in real time. View live log output in your terminal with a clean, developer-friendly CLI experience, making debugging and monitoring simpler.

View on GitHub
Deep Wiki
Context7

Getting Started

Install (requires PHP 8.1+, mbstring; the package is published under the Termwind\ namespace):

composer require nunomaduro/tailcli

First render — the entire public surface starts with one function:

use function Termwind\render;

render('<div class="px-2 py-1 bg-green-500 text-white font-bold">Deploy finished</div>');

Where to look first

  • README → "Usage" — the utility-class list is the vocabulary; there is no CSS reference to learn.
  • src/Termwind.php — the static entry points: render(), renderUsing(), style(), createElement(), parse(), width().
  • src/Elements/ — the tags you can actually use (div, span, p, a, hr, br, ul, ol, li, strong, em, pre, code, dl/dt/dd).
  • src/ValueObjects/ — how colors, spacing and widths are resolved when a class doesn't seem to do anything.

First use case — replacing $this->info() in an Artisan command with a padded result block:

class SyncCommand extends Command
{
    protected $signature = 'sync';

    public function handle(): int
    {
        // ... work ...

        render(<<<'HTML'
            <div class="px-2 py-1 bg-blue-900 text-white">
                <span class="font-bold">Synced</span> 1,204 records
            </div>
            <div class="mt-1 text-gray-500">Took 3.2s</div>
        HTML, $this->output);

        return self::SUCCESS;
    }
}

Passing $this->output is the important detail: output then respects --quiet, --no-ansi, and Artisan's test buffering.

Implementation Patterns

Compose output with small PHP helpers. The package has no component system, so your own functions become your components:

function badge(string $text, string $color = 'green'): string
{
    return sprintf(
        '<span class="px-1 bg-%s-500 text-white">%s</span>',
        $color,
        Termwind::escape($text)
    );
}

render('<div class="ml-1">' . badge('PASS') . ' migrations</div>');

Register named styles once in a service provider's boot() instead of repeating utility soup at every call site:

Termwind::style('ok')->apply('bg-green-500 text-white px-1');
Termwind::style('muted')->apply('text-gray-500');
render('<span class="ok">PASS</span> <span class="muted">2.4s</span>');

Use inline style="..." for one-offs. Inline declarations beat classes and are handy for dynamically computed spacing (style="margin-left: {$n}") where a class can't exist. Spacing values are in terminal cells, not rem.

Two-column rows with flex instead of hand-padding:

render(sprintf(
    '<div class="w-full flex justify-between"><span>%s</span><span class="text-green-500">%s</span></div>',
    e($label),
    e($value)
), $this->output);

Lists for grouped output — <ul> with <li class="ml-2"> renders bullets correctly across terminals, whereas manual - prefixes misalign with wide characters.

Custom elements for a tag you use everywhere: Termwind::createElement() lets you register a name that expands into your markup, keeping command code readable. It's the sanctioned extension point — reach for it before copy-pasting HTML strings across twenty commands.

Testing. Two options: swap the renderer with Termwind::renderUsing(...) to capture output, or assert on the parsed tree:

$output = Termwind::parse('<div class="mt-1">Hello</div>')->toString();
expect($output)->toContain('Hello');

Where it earns its keep in a Laravel app: long-running Artisan commands, scheduled-task reports, about-style diagnostics, installer/onboarding flows, and worker status lines. Keep these helpers in something like app/Console/Support/Output.php so they're testable without booting a command.

Gotchas and Tips

  • Escape everything user-supplied. Termwind::escape() (or htmlspecialchars) — a stray <, &, or unclosed tag silently swallows the rest of your markup. This is the number-one source of "my output disappeared".
  • Heredoc indentation leaks into layout. Termwind normalises whitespace, but leading indentation inside <div>s can shift px-*/ml-* results. Keep markup flush-left or trim() it.
  • w-full is the terminal width at render time. When output is piped (php artisan sync | cat) or run in CI, width can collapse to 80 or 0. Guard with Termwind::width() or an isDecorated() check.
  • ANSI is stripped for non-TTY output. Tests and redirected output get plain text — use --ansi or $this->output->setDecorated(true) when you need to eyeball formatting.
  • Wide characters count as two cells. CJK text and emoji break padding you compute yourself with strlen; rely on the package's width handling rather than substr/str_pad.
  • It is not a CSS engine. No borders, no grid, no arbitrary hex colors, no percentage widths beyond the bundled fractions, and only a subset of Tailwind utilities. Verify a class exists in the README before building a layout around it.
  • Don't declare conflicting utilities. text-red-500 text-green-500 in one class attribute has no defined winner — pick one, or use inline style and know it wins over classes.
  • Render once, not in a loop. Each render() call reparses HTML; build the full string and emit a single call for batch summaries.
  • render() echoes. When you need the string for a log file or an assertion, use Termwind::parse($html)->toString().
  • Styles are process-global. Registering them inside handle() works but is wasted work and can surprise other commands in the same process — put them in a provider.
  • Don't build prompts out of it. There's no interactivity; pair styled output with Laravel Prompts (note(), table(), confirm()) rather than hand-rolling input handling.
  • Debugging recipe: reduce to the smallest element that misbehaves, check Termwind::width(), and remember nested <div>s with px-* inside a w-full parent inflate total width and cause unexpected wrapping.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console