nunomaduro / termwind Goto Github PK
View Code? Open in Web Editor NEW๐ In short, it's like Tailwind CSS, but for the PHP command-line applications.
License: MIT License
๐ In short, it's like Tailwind CSS, but for the PHP command-line applications.
License: MIT License
Check this example
I am trying to use green-text color for the entire div. But after font-bold it resets all the styles.
Its because of \e[0m. Why are we resetting all the stylings?
Check this gist => https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
To reset each style it has its own number (i.e bold=> \e[22m, italic => \e[23m ...)
Is there any specific reason for going with \e[0m?
This is after I used the style specific ending sequences instead of \e[0m.
The $message variable is undefined.
And call to undefined method bgColor() . Its defined as bg(). Either update the readme or change the method name
I thinks it's a wrong behavior with div
elements.
Content of every div element should be on a new line.
<div>
<div>text text</div>
<div>text text</div>
</div>
Should render
text text
text text
Now it looks like
text texttext text
Would like to use in a few more advanced scenarios, but there's a few cases where line-wrapping and nesting elements cause different behaviors from what I'd expect in HTML:
<div class="m-1 p-1 bg-gray w-35">
Lorem ipsum dolor, sit amet consectetur adipisicing elit.
</div>
<div class="w-64 bg-gray p-1">
<span class="w-1/2 bg-red mr-1 p-1">
Left
</span>
<span class="w-1/2 bg-red p-1">
Right
</span>
</div>
Thanks again for this project!
I think something like this would be quite cool.
<div class="p-4 border-green-500">
Hello World!
</div>
โญโโโโโโโโโโโโโโโโโโโฎ
โ โ
โ Hello World! โ
โ โ
โฐโโโโโโโโโโโโโโโโโโโฏ
Hello, fantastic work on this library. The new Artisan improvements are great.
Problem - Using passthru()
produces jumbled output.
POC - Any Laravel console command that calls passthru
to call commands, so they do not share the main process.
public function handle()
{
passthru('composer update');
}
Using Laravel 9.20 - Output normal
Using Laravel 9.21 - Actual output
Using Laravel 9.21 - Expected output
Hello there,
Thank you so much for this amazing package. I am sorry to create and issue as this is more of a question.
Would it be possible to have a default answer for the ask() method? Something "a la" Symfony\Component\Console\Question\ConfirmationQuestion;
where the second param (I believe optional) is the default aswer in case the user press the 'return' key.
Let me know.
All the best
Eliott
Hi,
I was recently trying to use Termwind with Laravel Zero to make a small cli/php app.
After trying to print the first layout with Termwind, I can't get my head around the possibility to inject a variable in the template before rendering it.
Either I miss something, either I'm in a specific case (Laravel Zero, which has striped a lot of functionality of Laravel).
use function Termwind\render;
$test = 'hello';
render(<<<'HTML'
<div>
<div class="px-1 bg-green-600">test : </div>
<div class="ml-1">
{$test}<br>
$test<br>
${test}<br>
{{ $test }}<br>
<?php $test; <br>
</div>
</div>
HTML);
With this as an output :
test :
{$test}
$test
${test}
{{ $test }}
resources/views
and the file termwind.blade.php
with the template with the previous template.ViewServiceProvider
. 'providers' => [
App\Providers\AppServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
],
This was a good try but Laravel Zero just have a subset of laravel/framework
. Just the Foundation
folder not the View
one.
I could try to pull the whole laravel/framework
but I think it's not the way to go.
Here was the code I wanted to try :
use function Termwind\render;
$test = 'hello';
render(view('termwind', compact('test')));
use function Termwind\div;
use function Termwind\render;
$test = 'hello';
div([
div('test', 'px-1 bg-green-600'),
div($test, 'ml-1'),
])->render();
I get the following error :
Error
Call to undefined function Termwind\div()
I am actually now out of idea to try this. Any help appreciated!
I'm using :
Thanks for reading me and I'm hoping this can help others that want to use this wonderful tool you created !
After the PR #89, the render('text')
is converted to a <p>
tag by the DOMDocument
class.
Add RegExp to detect is there is no HTML on the string, and ignore the call of loadHtml
.
a href doesn't work in Windows 10 console and under wsl.
Is it working on a mac or Linux?
Is there some solution to avoid this problem?
I found that my
will take effect on the blank line, and this blank line will be applied with the background color, resulting in style confusion.
render(<<<'HTML'
<div class="mx-2 my-1 space-y-1">
<div class="bg-red"><p>xcccccccccccccccccccccccccccccccccccccccccccccccc</p></div>
</div>
HTML);
If this is a BUG, I am willing to fix it, but I may need some guidance.
A simple example of this issue can be found here: spatie/laravel-health#150.
It happens because Termwind has an static variable $renderer for it's output and when an Artisan call is made with a command that also uses Termwind this static variable is updated to use BufferedOutput instead of the default ConsoleOutput.
It would be great to render tables from HTML
Something like this
<table style="box">
<thead>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
</thead>
<tbody>
<tr>
<td><div class="width-5 px-4">4</div></td>
<td rowspan="2">9</td>
<td>6</td>
</tr>
<tr>
<td class="width-10">7</td>
<td colspan="2"><div>4</div></td>
</tr>
</tbody>
</table>
To this
โโโโโโโโโโโโโโโโโฌโโโโฌโโโโ
โ 1 โ 2 โ 3 โ
โโโโโโโโโโโโโโโโโผโโโโผโโโโค
โ 4 โ 9 โ 6 โ
โ 7 โ โ 4 โ
โโโโโโโโโโโโโโโโโดโโโโดโโโโ
I scetched a concept of TableRenderer
<?php
declare(strict_types=1);
namespace Termwind\Html;
use DOMNode;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableCell;
use Symfony\Component\Console\Output\BufferedOutput;
use Termwind\Components\Element;
use Termwind\HtmlRenderer;
use function Termwind\div;
final class TableRenderer
{
private Table $table;
private BufferedOutput $output;
private string $styles;
public function __construct(DOMNode $node)
{
$this->output = new BufferedOutput();
$this->table = new Table($this->output);
$style = $node->getAttribute('style');
$this->styles = $node->getAttribute('class');
if (!empty($style)) {
$this->table->setStyle($style);
}
$this->convert($node);
}
private function convert(DOMNode $node)
{
foreach ($node->childNodes as $child) {
if ($child->nodeName === 'thead') {
$this->parseHeader($child);
}
if ($child->nodeName === 'tbody') {
$this->parseBody($child);
}
if ($child->nodeName === 'tr') {
foreach ($this->parseRow($child) as $row) {
$this->table->addRow($row);
}
}
}
}
private function parseHeader(DOMNode $node)
{
foreach ($node->childNodes as $child) {
if ($child->nodeName === 'tr') {
foreach ($this->parseRow($child) as $row) {
$this->table->setHeaders($row);
}
}
}
}
private function parseBody(DOMNode $node)
{
foreach ($node->childNodes as $child) {
if ($child->nodeName === 'tr') {
foreach ($this->parseRow($child) as $row) {
$this->table->addRow($row);
}
}
}
}
private function parseRow(DOMNode $node): \Iterator
{
$row = [];
foreach ($node->childNodes as $child) {
if ($child->nodeName === 'th' || $child->nodeName === 'td') {
$row[] = new TableCell(
(string)(new HtmlRenderer())->parse($child->ownerDocument->saveXML($child)),
[
'colspan' => max((int)$child->getAttribute('colspan'), 1),
'rowspan' => max((int)$child->getAttribute('rowspan'), 1),
]
);
}
}
if ($row !== []) {
yield $row;
}
}
public function render(): Element
{
$this->table->render();
return div($this->output->fetch(), $this->styles);
}
}
And changes for HtmlRenderer
...
/**
* Convert a tree of DOM nodes to a tree of termwind elements.
*/
private function convert(DOMNode $node): Components\Element|string
{
...
if ($node->nodeName === 'table') {
return (new TableRenderer($node))->render();
}
...
}
...
I hope my sketch will be a good starting point for better solution with ability to use background colors and text colors in a table cell.
Thank you for an amazing package. I want to use it in my project, but I need tables as well.
Right now it's not possible to add styles to the <pre>
tag element.
Hi,
coming from a different project, which uses termwind to style command output: TomasVotruba/lines#35 (comment)
It's a symfony command, which uses build in options for coloring console output (or leave it as it is).
Using termwind as styling leads to ignoring the --no-ansi
option.
The screenshot shows the output, which shows ansi colors. That the colors are coliding with my iTerm2 agnoster theme leads me to that problem, that --no-ansi
is not working.
@butschster Find some issues.
Originally posted by @nunomaduro in #87 (comment)
I read your post on Twitter about using the span()
-method as a way to render lines. I feel a bit sad that it has been changed already. I hope to make a case to at least make line()
available aside any HTML-tags. And link
instead of a
.
echo
, grep
, awk
and others there is no mention of a span, a div or an a-tag. Every manual calls it a line, not a div.span
if it has a line break (again, a line). It's a block element. But why bother with semantics? It's a line, after all.Now that we're on the subject. You've also mentioned including h1. I personally don't like that. The command line doesn't know or care about headings. I would argue that if you need a heading, you'd extend line
(wouldn't a class for it make more sense?) and give it all its classes to be a header.
All grumpiness aside. The idea of using Tailwind colours (or any type of colour) is awesome. Oh, yea. Another small feature request: the Color
enum should probably be configurable :)
No matter what you decide, this is still a cool package that's going to be really useful.
Hi guys!
Let's imagine two situations
Fist
Two elements, each on a new line
<div>
<span class="px-1">hello</span>
<span class="px-1">world</span>
</div>
Second
Two element on one line with space
<div>
<span class="px-1">hello</span> <span class="px-1">world</span>
</div>
And what behavior we should expect on each case. I suggest the following behavior
In the first case we can remove empty space
hello world
In the second case we can keep space
hello world
Modern terminals allow users to configure a "theme", which is 16 predefined colors that work well together, and with the terminal background.
Most command line binaries use these 16 colors, so the output looks good in every theme. Here's msgcat
output in a light and dark terminal theme:
It would be awesome to make use of these in Termwind! That's the main thing keeping me from using it currently, since it feels less professional to show colors that may clash with the background (e.g. some people use a Solarized Light theme). I'm thinking something like text-ansi-red
/ bg-ansi-red
for the class names?
I have a following text and I want to print it as is without trimming.
# Introduction
The body of your message.
Thanks,
Laravel
ยฉ 2021 Laravel. All rights reserved.
It would be great to have <pre>
element
At this point, we already have an HTML-like API where you can do stuff like so:
use function Termwind\{div};
div('Hello World', 'ml-2')->render(); // renders " Hello World" on the console
The goal of this issue to create a new function with the name render
that accepts an HTML string and renders the console output like so:
use function Termwind\{render};
render('<div class="m-2">Hello World</div>'); // renders " Hello World" on the console
$trimedText = ltrim($node->textContent);
$text = preg_replace('/\s+/', ' ', $trimedText);
Now termwind can inherit width on two lines like:
Lines 213 to 222 in 9c030e6
But if add more than three lines, it will be missing after the last line space.
This unit test is failed:
$html = parse(<<<'HTML'
<div class="w-10">
<div class="w-full bg-red">AAA</div>
<div class="w-full bg-blue">BBB</div>
<div class="w-full bg-yellow">CCC</div>
</div>
HTML);
// this is expected
expect($html)->toBe("<bg=red>AAA </>\n<bg=blue>BBB </>\n<bg=yellow>CCC </>");
// // this is actual
// expect($html)->toBe("<bg=red>AAA </>\n<bg=blue>BBB </>\n<bg=yellow>CCC</>");
What do you think if use Highlighter from nunomaduro/collision
for rendering code snippets.
Something like this
<code line="18" start-line="14">
/** @test */
function sentryReport()
{
try {
throw new \Exception('Something went wrong');
} catch (\Throwable $e) {
report($e);
}
}
</code>
Will output
14โ /** @test */
15โ function sentryReport()
16โ {
17โ try {
โ 18โ throw new \Exception('Something went wrong');
19โ } catch (\Throwable $e) {
20โ report($e);
21โ }
22โ }
There is a promlem with content trimming at this line https://github.com/nunomaduro/termwind/blob/master/src/HtmlRenderer.php#L69
For example, when we use strong element inside div then a space between Hello
and world
will be deleted because of trim
function.
<p class="bg-red text-color-white">Hello <strong>world</strong></p>
The best solution is to use ltrim
I prepared a test for this problem
it('renders the element inside another element', function () {
$html = parse('<div>Hello <strong>world</strong></div>');
expect($html)->toBe("Hello \e[1mworld\e[0m");
});
Maybe it's a dumb question, but I didn't find any solutions.
Is it possible to disable termwind output when tests are running?
Let's say there is a simple command which outputs text with $this->info()
and render()
:
public function handle(): int
{
$this->info("Green text");
render('<div class="text-red-500">Red text</div>');
return self::SUCCESS;
}
and a simple test (Pest):
<?php
use App\Console\Commands\TestCommand;
use function Pest\Laravel\artisan;
it('returns a successful response', function () {
artisan(TestCommand::class)
->assertOk();
});
When tests are running, "Green text" will not show, while "Red text" will.
I think both should not be rendered while tests are running.
What's the best way to make render()
behave like standard output functions?
Thank you
TL;DR: Would you be ok with an added function, maybe something like asString
or something that just returns the final formatted string instead of echoing it (like render
)?
I created a function similar to Laravel prompts task
in CraftCms that shows either a check or an x depending on whether the task completed or not. However what I ran into was needing to know how many actual lines are output to the terminal, so I could move the cursor up that many lines and replace the text with an appended checkmark.
I ended up just directly using the HtmlRender
class like so to accomplish what I needed to.
$html = (new HtmlRenderer())->parse('<div>hi!</div>')->toString();
// count the lines
// replace the line with an appended check or x.
It's marked as internal and crossed out in my ide however. Not a huge deal, but I think just having a function similar to render
that just does the above would be a nice QoL improvement. So instead if people needed the raw string for whatever reason they could do
$string = asString('<div>hi!</div>');
instead of having to code dive.
I don't mind doing the pull request if you're ok with this.
I love the output, except that it doesn't render unicode entities like (✓
). Are you planning to add support for that?
Is it possible to test an Artisan command's console output (via expectsOutput
, doesntExpectOutput
) which is using, for example, render()
function?
Artisan command foo
:
...
render('bar'); // even with no styling/HTML
...
Unit PEST:
it('does not show output foo string', function () {
$this->artisan('foo')
->doesntExpectOutput('bar');
});
Apparently, there is no difference between using render('bar')
or not: the test always passes.
Am I doing anything wrong?
Thank you
Heyho,
when using flex to render columns besided each other and then applying padding to each of the boxes causes a weird result.
Working, without padding
render(<<<HTML
<div class="flex mt-1 justify-evenly">
<span class="flex-1 bg-green-100">
Today:
</span>
<span class="flex-1 bg-green-200">
Tomorrow:
</span>
</div>
HTML);
But when applying padding to the boxes they are not properly aligned anymore:
render(<<<HTML
<div class="flex mt-1 justify-evenly">
<span class="flex-1 bg-green-100 p-1">
Today:
</span>
<span class="flex-1 bg-green-200 p-1">
Tomorrow:
</span>
</div>
HTML);
Did I something wrong? I will try to look into the library to figure it out, but maybe I'm just using it wrong ๐ Thanks for the awesome library!
Hello everyone
I didn't know whether to create this issue in this repository or in the Laravel repository. I have created the issue here since in the latest version of Laravel this package is used under the hood for the refreshed UI.
New Laravel commands don't render properly when using iterm, for example, the new php artisan about
command:
Is there something I have to configure beforehand or am I missing something?
Thanks a lot
There is only one way to increase amount of elements in termwind - Add PRs with new features.
But sometimes requires an ability to extend HtmlRenderer for specific project.
Something like this
HtmlRenderer::extend('code', static function(\DOMNode $node): Raw {
$highlighter = new Highlighter();
$line = (int) $node->getAttribute('line');
$html = array_reduce(
iterator_to_array($node->childNodes),
static fn(string $html, \DOMNode $child) => $html .= $child->ownerDocument->saveXML($child),
''
);
$html = html_entity_decode($html);
return Termwind::raw(
$highlighter->highlight(html_entity_decode($html), $line)
);
});
As you understood, this package fits me on 100%. I have a pet project where one of the features is rendering debug output into a terminal and I have a lot of legacy code I'm trying to move to this package.
Really awesome project, thanks for creating it. I had a quick question related to colors.
According to the docs we should be able to use any of the colors within Tailwind CSS, but it seems like not all variations or colors are supported? I do not remember running into this issue during other previous projects which used Termwind. I am wondering if there is another issue at play here.
Are the specific supported colors related to the terminal software you are using? Bash vs. ZSH? or maybe other terminal settings/plugins/themes? Or is there a more refined list of colors which Termwind supports that is not as extensive as everything available via Tailwind.
When HtmlRenerder detects element for given tag and it doesn't find it, it uses a default element (https://github.com/nunomaduro/termwind/blob/master/src/HtmlRenderer.php#L86). I think, default element should take styles.
Here is an example where you can see the problem.
it('renders the element inside another element', function () {
$html = parse('<p class="bg-red text-color-white">Hello <strong>world</strong></p>');
expect($html)->toBe("Hello \e[1mworld\e[0m");
});
Default colors are not being applied to html element on windows terminal (using windows sub system for linux).
Also the parser trying to parse special characters as html, in this case <== gives an warning.
require __DIR__ . '/vendor/autoload.php';
use function Termwind\{render};
// single line html...
render('<div class="p-1 bg-green-300">Termwind</div><span class="text-red-200"><== this was supposed to be green.<span>');
@nunomaduro maybe do you have idea?
TypeError: Termwind\Termwind::div(): Return value must be of type Termwind\Components\Div, Termwind\Components\Div returned
When running vendor:publish
in Laravel following error is thrown
Which provider or tag's files would you like to publish?
Error
Call to undefined function Termwind\ValueObjects\mb_strimwidth()
at vendor/nunomaduro/termwind/src/ValueObjects/Styles.php:1053
1049โ */
1050โ private static function trimText(string $text, int $width): string
1051โ {
1052โ preg_match_all(self::STYLING_REGEX, $text, $matches, PREG_OFFSET_CAPTURE);
โ 1053โ $text = rtrim(mb_strimwidth(preg_replace(self::STYLING_REGEX, '', $text) ?? '', 0, $width, '', 'UTF-8'));
1054โ
1055โ foreach ($matches[0] ?? [] as [$part, $index]) {
1056โ $text = substr($text, 0, $index).$part.substr($text, $index, null);
1057โ }
+4 vendor frames
5 [internal]:0
Termwind\Components\Element::__toString()
+32 vendor frames
38 artisan:37
Illuminate\Foundation\Console\Kernel::handle()
Hello ๐
I'm using this package as part of Laravel Zero. Within my application, If I run schedule:work
, it'll start successfully but then sometimes after ~10 seconds, fail with:
Symfony\Component\ErrorHandler\Error\FatalError: Allowed memory size of 134217728 bytes exhausted (tried to allocate 4096 bytes)
Looking at the error in our Sentry instance, the error seems to be in /vendor/nunomaduro/termwind/src/ValueObjects/Styles.php and is:
public function with(array $properties): self
{
$this->properties = array_replace_recursive($this->properties, $properties);
return $this;
}
As an additional note, we've not actually done anything with Termwind in the application. The only Termwind usage I can see is in the default InspireCommand
class, but that has the schedule commented out so it's not even included when running schedule:work
.
I'd appreciate someone looking into this, if you need any more information, let me know!
Thanks
Since #52 was merged, classes like font-bold
italic
underline
line-through
invisible
are wrapping the content of the element with escape codes.
But classes like uppercase
, truncate
, snacecase
are transforming the content of the element.
So when these classes are combined, e.g. font-bold uppercase
, the escape codes will be capitalized which breaks them.
Also, if the classes are set in reverse order, e.g. uppercase font-bold
this does not happen because the classes are applied in the order they appear (it should not).
E.g. <div class="font-bold uppercase">text</div>
renders <bg=default;options=>\e[1MTEXT\e[0M</>
instead of <bg=default;options=>\e[1mTEXT\e[0m</>
.
I'm using spatie/laravel-health which requires nunomaduro/termwind 1.1 but receveing the following error.
I can not figure out why. It's not happening in dev environment but when I try and deploy to staging environment on Laravel Forge.
PHP Parse error: syntax error, unexpected '|', expecting variable (T_VARIABLE) in /home/user/site/vendor/nunomaduro/termwind/src/Functions.php on line 17
There is a pipe on line 17 but not sure on usage, I presume its used correctly?
Line 17 in 2a04769
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.