🎉
This commit is contained in:
206
vendor/laravel/prompts/src/Concerns/Colors.php
vendored
Normal file
206
vendor/laravel/prompts/src/Concerns/Colors.php
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts\Concerns;
|
||||
|
||||
trait Colors
|
||||
{
|
||||
/**
|
||||
* Reset all colors and styles.
|
||||
*/
|
||||
public function reset(string $text): string
|
||||
{
|
||||
return "\e[0m{$text}\e[0m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the text bold.
|
||||
*/
|
||||
public function bold(string $text): string
|
||||
{
|
||||
return "\e[1m{$text}\e[22m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the text dim.
|
||||
*/
|
||||
public function dim(string $text): string
|
||||
{
|
||||
return "\e[2m{$text}\e[22m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the text italic.
|
||||
*/
|
||||
public function italic(string $text): string
|
||||
{
|
||||
return "\e[3m{$text}\e[23m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Underline the text.
|
||||
*/
|
||||
public function underline(string $text): string
|
||||
{
|
||||
return "\e[4m{$text}\e[24m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Invert the text and background colors.
|
||||
*/
|
||||
public function inverse(string $text): string
|
||||
{
|
||||
return "\e[7m{$text}\e[27m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the text.
|
||||
*/
|
||||
public function hidden(string $text): string
|
||||
{
|
||||
return "\e[8m{$text}\e[28m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Strike through the text.
|
||||
*/
|
||||
public function strikethrough(string $text): string
|
||||
{
|
||||
return "\e[9m{$text}\e[29m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text color to black.
|
||||
*/
|
||||
public function black(string $text): string
|
||||
{
|
||||
return "\e[30m{$text}\e[39m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text color to red.
|
||||
*/
|
||||
public function red(string $text): string
|
||||
{
|
||||
return "\e[31m{$text}\e[39m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text color to green.
|
||||
*/
|
||||
public function green(string $text): string
|
||||
{
|
||||
return "\e[32m{$text}\e[39m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text color to yellow.
|
||||
*/
|
||||
public function yellow(string $text): string
|
||||
{
|
||||
return "\e[33m{$text}\e[39m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text color to blue.
|
||||
*/
|
||||
public function blue(string $text): string
|
||||
{
|
||||
return "\e[34m{$text}\e[39m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text color to magenta.
|
||||
*/
|
||||
public function magenta(string $text): string
|
||||
{
|
||||
return "\e[35m{$text}\e[39m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text color to cyan.
|
||||
*/
|
||||
public function cyan(string $text): string
|
||||
{
|
||||
return "\e[36m{$text}\e[39m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text color to white.
|
||||
*/
|
||||
public function white(string $text): string
|
||||
{
|
||||
return "\e[37m{$text}\e[39m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text background to black.
|
||||
*/
|
||||
public function bgBlack(string $text): string
|
||||
{
|
||||
return "\e[40m{$text}\e[49m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text background to red.
|
||||
*/
|
||||
public function bgRed(string $text): string
|
||||
{
|
||||
return "\e[41m{$text}\e[49m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text background to green.
|
||||
*/
|
||||
public function bgGreen(string $text): string
|
||||
{
|
||||
return "\e[42m{$text}\e[49m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text background to yellow.
|
||||
*/
|
||||
public function bgYellow(string $text): string
|
||||
{
|
||||
return "\e[43m{$text}\e[49m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text background to blue.
|
||||
*/
|
||||
public function bgBlue(string $text): string
|
||||
{
|
||||
return "\e[44m{$text}\e[49m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text background to magenta.
|
||||
*/
|
||||
public function bgMagenta(string $text): string
|
||||
{
|
||||
return "\e[45m{$text}\e[49m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text background to cyan.
|
||||
*/
|
||||
public function bgCyan(string $text): string
|
||||
{
|
||||
return "\e[46m{$text}\e[49m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text background to white.
|
||||
*/
|
||||
public function bgWhite(string $text): string
|
||||
{
|
||||
return "\e[47m{$text}\e[49m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text color to gray.
|
||||
*/
|
||||
public function gray(string $text): string
|
||||
{
|
||||
return "\e[90m{$text}\e[39m";
|
||||
}
|
||||
}
|
||||
63
vendor/laravel/prompts/src/Concerns/Cursor.php
vendored
Normal file
63
vendor/laravel/prompts/src/Concerns/Cursor.php
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts\Concerns;
|
||||
|
||||
trait Cursor
|
||||
{
|
||||
/**
|
||||
* Indicates if the cursor has been hidden.
|
||||
*/
|
||||
protected static bool $cursorHidden = false;
|
||||
|
||||
/**
|
||||
* Hide the cursor.
|
||||
*/
|
||||
public function hideCursor(): void
|
||||
{
|
||||
static::writeDirectly("\e[?25l");
|
||||
|
||||
static::$cursorHidden = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the cursor.
|
||||
*/
|
||||
public function showCursor(): void
|
||||
{
|
||||
static::writeDirectly("\e[?25h");
|
||||
|
||||
static::$cursorHidden = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the cursor if it was hidden.
|
||||
*/
|
||||
public function restoreCursor(): void
|
||||
{
|
||||
if (static::$cursorHidden) {
|
||||
$this->showCursor();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the cursor.
|
||||
*/
|
||||
public function moveCursor(int $x, int $y = 0): void
|
||||
{
|
||||
$sequence = '';
|
||||
|
||||
if ($x < 0) {
|
||||
$sequence .= "\e[".abs($x).'D'; // Left
|
||||
} elseif ($x > 0) {
|
||||
$sequence .= "\e[{$x}C"; // Right
|
||||
}
|
||||
|
||||
if ($y < 0) {
|
||||
$sequence .= "\e[".abs($y).'A'; // Up
|
||||
} elseif ($y > 0) {
|
||||
$sequence .= "\e[{$y}B"; // Down
|
||||
}
|
||||
|
||||
static::writeDirectly($sequence);
|
||||
}
|
||||
}
|
||||
31
vendor/laravel/prompts/src/Concerns/Erase.php
vendored
Normal file
31
vendor/laravel/prompts/src/Concerns/Erase.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts\Concerns;
|
||||
|
||||
trait Erase
|
||||
{
|
||||
/**
|
||||
* Erase the given number of lines downwards from the cursor position.
|
||||
*/
|
||||
public function eraseLines(int $count): void
|
||||
{
|
||||
$clear = '';
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$clear .= "\e[2K".($i < $count - 1 ? "\e[{$count}A" : '');
|
||||
}
|
||||
|
||||
if ($count) {
|
||||
$clear .= "\e[G";
|
||||
}
|
||||
|
||||
static::writeDirectly($clear);
|
||||
}
|
||||
|
||||
/**
|
||||
* Erase from cursor until end of screen.
|
||||
*/
|
||||
public function eraseDown(): void
|
||||
{
|
||||
static::writeDirectly("\e[J");
|
||||
}
|
||||
}
|
||||
41
vendor/laravel/prompts/src/Concerns/Events.php
vendored
Normal file
41
vendor/laravel/prompts/src/Concerns/Events.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts\Concerns;
|
||||
|
||||
use Closure;
|
||||
|
||||
trait Events
|
||||
{
|
||||
/**
|
||||
* The registered event listeners.
|
||||
*
|
||||
* @var array<string, array<int, Closure>>
|
||||
*/
|
||||
protected array $listeners = [];
|
||||
|
||||
/**
|
||||
* Register an event listener.
|
||||
*/
|
||||
public function on(string $event, Closure $callback): void
|
||||
{
|
||||
$this->listeners[$event][] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit an event.
|
||||
*/
|
||||
public function emit(string $event, mixed ...$data): void
|
||||
{
|
||||
foreach ($this->listeners[$event] ?? [] as $listener) {
|
||||
$listener(...$data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean the event listeners.
|
||||
*/
|
||||
public function clearListeners(): void
|
||||
{
|
||||
$this->listeners = [];
|
||||
}
|
||||
}
|
||||
91
vendor/laravel/prompts/src/Concerns/FakesInputOutput.php
vendored
Normal file
91
vendor/laravel/prompts/src/Concerns/FakesInputOutput.php
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts\Concerns;
|
||||
|
||||
use Laravel\Prompts\Output\BufferedConsoleOutput;
|
||||
use Laravel\Prompts\Terminal;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use RuntimeException;
|
||||
|
||||
trait FakesInputOutput
|
||||
{
|
||||
/**
|
||||
* Fake the terminal and queue key presses to be simulated.
|
||||
*
|
||||
* @param array<string> $keys
|
||||
*/
|
||||
public static function fake(array $keys = []): void
|
||||
{
|
||||
// Force interactive mode when testing because we will be mocking the terminal.
|
||||
static::interactive();
|
||||
|
||||
$mock = \Mockery::mock(Terminal::class);
|
||||
|
||||
$mock->shouldReceive('write')->byDefault();
|
||||
$mock->shouldReceive('exit')->byDefault();
|
||||
$mock->shouldReceive('setTty')->byDefault();
|
||||
$mock->shouldReceive('restoreTty')->byDefault();
|
||||
$mock->shouldReceive('cols')->byDefault()->andReturn(80);
|
||||
$mock->shouldReceive('lines')->byDefault()->andReturn(24);
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$mock->shouldReceive('read')->once()->andReturn($key);
|
||||
}
|
||||
|
||||
static::$terminal = $mock;
|
||||
|
||||
self::setOutput(new BufferedConsoleOutput());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the output contains the given string.
|
||||
*/
|
||||
public static function assertOutputContains(string $string): void
|
||||
{
|
||||
Assert::assertStringContainsString($string, static::content());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the output doesn't contain the given string.
|
||||
*/
|
||||
public static function assertOutputDoesntContain(string $string): void
|
||||
{
|
||||
Assert::assertStringNotContainsString($string, static::content());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the stripped output contains the given string.
|
||||
*/
|
||||
public static function assertStrippedOutputContains(string $string): void
|
||||
{
|
||||
Assert::assertStringContainsString($string, static::strippedContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the stripped output doesn't contain the given string.
|
||||
*/
|
||||
public static function assertStrippedOutputDoesntContain(string $string): void
|
||||
{
|
||||
Assert::assertStringNotContainsString($string, static::strippedContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the buffered console output.
|
||||
*/
|
||||
public static function content(): string
|
||||
{
|
||||
if (! static::output() instanceof BufferedConsoleOutput) {
|
||||
throw new RuntimeException('Prompt must be faked before accessing content.');
|
||||
}
|
||||
|
||||
return static::output()->content();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the buffered console output, stripped of escape sequences.
|
||||
*/
|
||||
public static function strippedContent(): string
|
||||
{
|
||||
return preg_replace("/\e\[[0-9;?]*[A-Za-z]/", '', static::content());
|
||||
}
|
||||
}
|
||||
61
vendor/laravel/prompts/src/Concerns/Fallback.php
vendored
Normal file
61
vendor/laravel/prompts/src/Concerns/Fallback.php
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts\Concerns;
|
||||
|
||||
use Closure;
|
||||
use RuntimeException;
|
||||
|
||||
trait Fallback
|
||||
{
|
||||
/**
|
||||
* Whether to fallback to a custom implementation
|
||||
*/
|
||||
protected static bool $shouldFallback = false;
|
||||
|
||||
/**
|
||||
* The fallback implementations.
|
||||
*
|
||||
* @var array<class-string, Closure($this): mixed>
|
||||
*/
|
||||
protected static array $fallbacks = [];
|
||||
|
||||
/**
|
||||
* Enable the fallback implementation.
|
||||
*/
|
||||
public static function fallbackWhen(bool $condition): void
|
||||
{
|
||||
static::$shouldFallback = $condition || static::$shouldFallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the prompt should fallback to a custom implementation.
|
||||
*/
|
||||
public static function shouldFallback(): bool
|
||||
{
|
||||
return static::$shouldFallback && isset(static::$fallbacks[static::class]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the fallback implementation.
|
||||
*
|
||||
* @param Closure($this): mixed $fallback
|
||||
*/
|
||||
public static function fallbackUsing(Closure $fallback): void
|
||||
{
|
||||
static::$fallbacks[static::class] = $fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the registered fallback implementation.
|
||||
*/
|
||||
public function fallback(): mixed
|
||||
{
|
||||
$fallback = static::$fallbacks[static::class] ?? null;
|
||||
|
||||
if ($fallback === null) {
|
||||
throw new RuntimeException('No fallback implementation registered for ['.static::class.']');
|
||||
}
|
||||
|
||||
return $fallback($this);
|
||||
}
|
||||
}
|
||||
37
vendor/laravel/prompts/src/Concerns/Interactivity.php
vendored
Normal file
37
vendor/laravel/prompts/src/Concerns/Interactivity.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts\Concerns;
|
||||
|
||||
use Laravel\Prompts\Exceptions\NonInteractiveValidationException;
|
||||
|
||||
trait Interactivity
|
||||
{
|
||||
/**
|
||||
* Whether to render the prompt interactively.
|
||||
*/
|
||||
protected static bool $interactive;
|
||||
|
||||
/**
|
||||
* Set interactive mode.
|
||||
*/
|
||||
public static function interactive(bool $interactive = true): void
|
||||
{
|
||||
static::$interactive = $interactive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default value if it passes validation.
|
||||
*/
|
||||
protected function default(): mixed
|
||||
{
|
||||
$default = $this->value();
|
||||
|
||||
$this->validate($default);
|
||||
|
||||
if ($this->state === 'error') {
|
||||
throw new NonInteractiveValidationException($this->error);
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
115
vendor/laravel/prompts/src/Concerns/Scrolling.php
vendored
Normal file
115
vendor/laravel/prompts/src/Concerns/Scrolling.php
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts\Concerns;
|
||||
|
||||
use Laravel\Prompts\Themes\Contracts\Scrolling as ScrollingRenderer;
|
||||
|
||||
trait Scrolling
|
||||
{
|
||||
/**
|
||||
* The number of items to display before scrolling.
|
||||
*/
|
||||
public int $scroll;
|
||||
|
||||
/**
|
||||
* The index of the highlighted option.
|
||||
*/
|
||||
public ?int $highlighted;
|
||||
|
||||
/**
|
||||
* The index of the first visible option.
|
||||
*/
|
||||
public int $firstVisible = 0;
|
||||
|
||||
/**
|
||||
* Initialize scrolling.
|
||||
*/
|
||||
protected function initializeScrolling(?int $highlighted = null): void
|
||||
{
|
||||
$this->highlighted = $highlighted;
|
||||
|
||||
$this->reduceScrollingToFitTerminal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce the scroll property to fit the terminal height.
|
||||
*/
|
||||
protected function reduceScrollingToFitTerminal(): void
|
||||
{
|
||||
$reservedLines = ($renderer = $this->getRenderer()) instanceof ScrollingRenderer ? $renderer->reservedLines() : 0;
|
||||
|
||||
$this->scroll = min($this->scroll, $this->terminal()->lines() - $reservedLines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight the given index.
|
||||
*/
|
||||
protected function highlight(?int $index): void
|
||||
{
|
||||
$this->highlighted = $index;
|
||||
|
||||
if ($this->highlighted === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->highlighted < $this->firstVisible) {
|
||||
$this->firstVisible = $this->highlighted;
|
||||
} elseif ($this->highlighted > $this->firstVisible + $this->scroll - 1) {
|
||||
$this->firstVisible = $this->highlighted - $this->scroll + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight the previous entry, or wrap around to the last entry.
|
||||
*/
|
||||
protected function highlightPrevious(int $total, bool $allowNull = false): void
|
||||
{
|
||||
if ($total === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->highlighted === null) {
|
||||
$this->highlight($total - 1);
|
||||
} elseif ($this->highlighted === 0) {
|
||||
$this->highlight($allowNull ? null : ($total - 1));
|
||||
} else {
|
||||
$this->highlight($this->highlighted - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight the next entry, or wrap around to the first entry.
|
||||
*/
|
||||
protected function highlightNext(int $total, bool $allowNull = false): void
|
||||
{
|
||||
if ($total === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->highlighted === $total - 1) {
|
||||
$this->highlight($allowNull ? null : 0);
|
||||
} else {
|
||||
$this->highlight(($this->highlighted ?? -1) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Center the highlighted option.
|
||||
*/
|
||||
protected function scrollToHighlighted(int $total): void
|
||||
{
|
||||
if ($this->highlighted < $this->scroll) {
|
||||
return;
|
||||
}
|
||||
|
||||
$remaining = $total - $this->highlighted - 1;
|
||||
$halfScroll = (int) floor($this->scroll / 2);
|
||||
$endOffset = max(0, $halfScroll - $remaining);
|
||||
|
||||
if ($this->scroll % 2 === 0) {
|
||||
$endOffset--;
|
||||
}
|
||||
|
||||
$this->firstVisible = $this->highlighted - $halfScroll - $endOffset;
|
||||
}
|
||||
}
|
||||
25
vendor/laravel/prompts/src/Concerns/Termwind.php
vendored
Normal file
25
vendor/laravel/prompts/src/Concerns/Termwind.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts\Concerns;
|
||||
|
||||
use Laravel\Prompts\Output\BufferedConsoleOutput;
|
||||
|
||||
use function Termwind\render;
|
||||
use function Termwind\renderUsing;
|
||||
|
||||
trait Termwind
|
||||
{
|
||||
protected function termwind(string $html)
|
||||
{
|
||||
renderUsing($output = new BufferedConsoleOutput());
|
||||
|
||||
render($html);
|
||||
|
||||
return $this->restoreEscapeSequences($output->fetch());
|
||||
}
|
||||
|
||||
protected function restoreEscapeSequences(string $string)
|
||||
{
|
||||
return preg_replace('/\[(\d+)m/', "\e[".'\1m', $string);
|
||||
}
|
||||
}
|
||||
111
vendor/laravel/prompts/src/Concerns/Themes.php
vendored
Normal file
111
vendor/laravel/prompts/src/Concerns/Themes.php
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts\Concerns;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Laravel\Prompts\ConfirmPrompt;
|
||||
use Laravel\Prompts\MultiSearchPrompt;
|
||||
use Laravel\Prompts\MultiSelectPrompt;
|
||||
use Laravel\Prompts\Note;
|
||||
use Laravel\Prompts\PasswordPrompt;
|
||||
use Laravel\Prompts\Progress;
|
||||
use Laravel\Prompts\SearchPrompt;
|
||||
use Laravel\Prompts\SelectPrompt;
|
||||
use Laravel\Prompts\Spinner;
|
||||
use Laravel\Prompts\SuggestPrompt;
|
||||
use Laravel\Prompts\Table;
|
||||
use Laravel\Prompts\TextPrompt;
|
||||
use Laravel\Prompts\Themes\Default\ConfirmPromptRenderer;
|
||||
use Laravel\Prompts\Themes\Default\MultiSearchPromptRenderer;
|
||||
use Laravel\Prompts\Themes\Default\MultiSelectPromptRenderer;
|
||||
use Laravel\Prompts\Themes\Default\NoteRenderer;
|
||||
use Laravel\Prompts\Themes\Default\PasswordPromptRenderer;
|
||||
use Laravel\Prompts\Themes\Default\ProgressRenderer;
|
||||
use Laravel\Prompts\Themes\Default\SearchPromptRenderer;
|
||||
use Laravel\Prompts\Themes\Default\SelectPromptRenderer;
|
||||
use Laravel\Prompts\Themes\Default\SpinnerRenderer;
|
||||
use Laravel\Prompts\Themes\Default\SuggestPromptRenderer;
|
||||
use Laravel\Prompts\Themes\Default\TableRenderer;
|
||||
use Laravel\Prompts\Themes\Default\TextPromptRenderer;
|
||||
|
||||
trait Themes
|
||||
{
|
||||
/**
|
||||
* The name of the active theme.
|
||||
*/
|
||||
protected static string $theme = 'default';
|
||||
|
||||
/**
|
||||
* The available themes.
|
||||
*
|
||||
* @var array<string, array<class-string<\Laravel\Prompts\Prompt>, class-string<object&callable>>>
|
||||
*/
|
||||
protected static array $themes = [
|
||||
'default' => [
|
||||
TextPrompt::class => TextPromptRenderer::class,
|
||||
PasswordPrompt::class => PasswordPromptRenderer::class,
|
||||
SelectPrompt::class => SelectPromptRenderer::class,
|
||||
MultiSelectPrompt::class => MultiSelectPromptRenderer::class,
|
||||
ConfirmPrompt::class => ConfirmPromptRenderer::class,
|
||||
SearchPrompt::class => SearchPromptRenderer::class,
|
||||
MultiSearchPrompt::class => MultiSearchPromptRenderer::class,
|
||||
SuggestPrompt::class => SuggestPromptRenderer::class,
|
||||
Spinner::class => SpinnerRenderer::class,
|
||||
Note::class => NoteRenderer::class,
|
||||
Table::class => TableRenderer::class,
|
||||
Progress::class => ProgressRenderer::class,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Get or set the active theme.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function theme(?string $name = null): string
|
||||
{
|
||||
if ($name === null) {
|
||||
return static::$theme;
|
||||
}
|
||||
|
||||
if (! isset(static::$themes[$name])) {
|
||||
throw new InvalidArgumentException("Prompt theme [{$name}] not found.");
|
||||
}
|
||||
|
||||
return static::$theme = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new theme.
|
||||
*
|
||||
* @param array<class-string<\Laravel\Prompts\Prompt>, class-string<object&callable>> $renderers
|
||||
*/
|
||||
public static function addTheme(string $name, array $renderers): void
|
||||
{
|
||||
if ($name === 'default') {
|
||||
throw new InvalidArgumentException('The default theme cannot be overridden.');
|
||||
}
|
||||
|
||||
static::$themes[$name] = $renderers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the renderer for the current prompt.
|
||||
*/
|
||||
protected function getRenderer(): callable
|
||||
{
|
||||
$class = get_class($this);
|
||||
|
||||
return new (static::$themes[static::$theme][$class] ?? static::$themes['default'][$class])($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the prompt using the active theme.
|
||||
*/
|
||||
protected function renderTheme(): string
|
||||
{
|
||||
$renderer = $this->getRenderer();
|
||||
|
||||
return $renderer($this);
|
||||
}
|
||||
}
|
||||
20
vendor/laravel/prompts/src/Concerns/Truncation.php
vendored
Normal file
20
vendor/laravel/prompts/src/Concerns/Truncation.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts\Concerns;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
trait Truncation
|
||||
{
|
||||
/**
|
||||
* Truncate a value with an ellipsis if it exceeds the given width.
|
||||
*/
|
||||
protected function truncate(string $string, int $width): string
|
||||
{
|
||||
if ($width <= 0) {
|
||||
throw new InvalidArgumentException("Width [{$width}] must be greater than zero.");
|
||||
}
|
||||
|
||||
return mb_strwidth($string) <= $width ? $string : (mb_strimwidth($string, 0, $width - 1).'…');
|
||||
}
|
||||
}
|
||||
120
vendor/laravel/prompts/src/Concerns/TypedValue.php
vendored
Normal file
120
vendor/laravel/prompts/src/Concerns/TypedValue.php
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts\Concerns;
|
||||
|
||||
use Laravel\Prompts\Key;
|
||||
|
||||
trait TypedValue
|
||||
{
|
||||
/**
|
||||
* The value that has been typed.
|
||||
*/
|
||||
protected string $typedValue = '';
|
||||
|
||||
/**
|
||||
* The position of the virtual cursor.
|
||||
*/
|
||||
protected int $cursorPosition = 0;
|
||||
|
||||
/**
|
||||
* Track the value as the user types.
|
||||
*/
|
||||
protected function trackTypedValue(string $default = '', bool $submit = true, ?callable $ignore = null): void
|
||||
{
|
||||
$this->typedValue = $default;
|
||||
|
||||
if ($this->typedValue) {
|
||||
$this->cursorPosition = mb_strlen($this->typedValue);
|
||||
}
|
||||
|
||||
$this->on('key', function ($key) use ($submit, $ignore) {
|
||||
if ($key[0] === "\e" || in_array($key, [Key::CTRL_B, Key::CTRL_F, Key::CTRL_A, Key::CTRL_E])) {
|
||||
if ($ignore !== null && $ignore($key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
match ($key) {
|
||||
Key::LEFT, Key::LEFT_ARROW, Key::CTRL_B => $this->cursorPosition = max(0, $this->cursorPosition - 1),
|
||||
Key::RIGHT, Key::RIGHT_ARROW, Key::CTRL_F => $this->cursorPosition = min(mb_strlen($this->typedValue), $this->cursorPosition + 1),
|
||||
Key::oneOf([Key::HOME, Key::CTRL_A], $key) => $this->cursorPosition = 0,
|
||||
Key::oneOf([Key::END, Key::CTRL_E], $key) => $this->cursorPosition = mb_strlen($this->typedValue),
|
||||
Key::DELETE => $this->typedValue = mb_substr($this->typedValue, 0, $this->cursorPosition).mb_substr($this->typedValue, $this->cursorPosition + 1),
|
||||
default => null,
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Keys may be buffered.
|
||||
foreach (mb_str_split($key) as $key) {
|
||||
if ($ignore !== null && $ignore($key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($key === Key::ENTER && $submit) {
|
||||
$this->submit();
|
||||
|
||||
return;
|
||||
} elseif ($key === Key::BACKSPACE || $key === Key::CTRL_H) {
|
||||
if ($this->cursorPosition === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->typedValue = mb_substr($this->typedValue, 0, $this->cursorPosition - 1).mb_substr($this->typedValue, $this->cursorPosition);
|
||||
$this->cursorPosition--;
|
||||
} elseif (ord($key) >= 32) {
|
||||
$this->typedValue = mb_substr($this->typedValue, 0, $this->cursorPosition).$key.mb_substr($this->typedValue, $this->cursorPosition);
|
||||
$this->cursorPosition++;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the prompt.
|
||||
*/
|
||||
public function value(): string
|
||||
{
|
||||
return $this->typedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a virtual cursor to the value and truncate if necessary.
|
||||
*/
|
||||
protected function addCursor(string $value, int $cursorPosition, int $maxWidth): string
|
||||
{
|
||||
$before = mb_substr($value, 0, $cursorPosition);
|
||||
$current = mb_substr($value, $cursorPosition, 1);
|
||||
$after = mb_substr($value, $cursorPosition + 1);
|
||||
|
||||
$cursor = mb_strlen($current) ? $current : ' ';
|
||||
|
||||
$spaceBefore = $maxWidth - mb_strwidth($cursor) - (mb_strwidth($after) > 0 ? 1 : 0);
|
||||
[$truncatedBefore, $wasTruncatedBefore] = mb_strwidth($before) > $spaceBefore
|
||||
? [$this->trimWidthBackwards($before, 0, $spaceBefore - 1), true]
|
||||
: [$before, false];
|
||||
|
||||
$spaceAfter = $maxWidth - ($wasTruncatedBefore ? 1 : 0) - mb_strwidth($truncatedBefore) - mb_strwidth($cursor);
|
||||
[$truncatedAfter, $wasTruncatedAfter] = mb_strwidth($after) > $spaceAfter
|
||||
? [mb_strimwidth($after, 0, $spaceAfter - 1), true]
|
||||
: [$after, false];
|
||||
|
||||
return ($wasTruncatedBefore ? $this->dim('…') : '')
|
||||
.$truncatedBefore
|
||||
.$this->inverse($cursor)
|
||||
.$truncatedAfter
|
||||
.($wasTruncatedAfter ? $this->dim('…') : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a truncated string with the specified width from the end.
|
||||
*/
|
||||
private function trimWidthBackwards(string $string, int $start, int $width): string
|
||||
{
|
||||
$reversed = implode('', array_reverse(mb_str_split($string, 1)));
|
||||
|
||||
$trimmed = mb_strimwidth($reversed, $start, $width);
|
||||
|
||||
return implode('', array_reverse(mb_str_split($trimmed, 1)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user