Import latest app updates from streamline

An updated set of source files and initialization was provided by streamline to
address issues observed during initial testing. These files have been updated in
order to generate a new set of app images.
This commit is contained in:
2024-04-11 11:16:51 -07:00
parent cb4c6b4c1a
commit 4a89356390
10435 changed files with 107737 additions and 1220538 deletions
@@ -1,206 +0,0 @@
<?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";
}
}
@@ -1,63 +0,0 @@
<?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);
}
}
@@ -1,31 +0,0 @@
<?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");
}
}
@@ -1,41 +0,0 @@
<?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 = [];
}
}
@@ -1,91 +0,0 @@
<?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());
}
}
@@ -1,61 +0,0 @@
<?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);
}
}
@@ -1,37 +0,0 @@
<?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;
}
}
@@ -1,115 +0,0 @@
<?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;
}
}
@@ -1,25 +0,0 @@
<?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);
}
}
@@ -1,114 +0,0 @@
<?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\PausePrompt;
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\PausePromptRenderer;
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,
PausePrompt::class => PausePromptRenderer::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);
}
}
@@ -1,20 +0,0 @@
<?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).'…');
}
}
@@ -1,120 +0,0 @@
<?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)));
}
}