diff --git a/src/Concerns/Cursor.php b/src/Concerns/Cursor.php
index 1a2f1e17..0dcb5f10 100644
--- a/src/Concerns/Cursor.php
+++ b/src/Concerns/Cursor.php
@@ -14,7 +14,7 @@ trait Cursor
*/
public function hideCursor(): void
{
- static::writeDirectly("\e[?25l");
+ static::writer()->writeDirectly("\e[?25l");
static::$cursorHidden = true;
}
@@ -24,7 +24,7 @@ public function hideCursor(): void
*/
public function showCursor(): void
{
- static::writeDirectly("\e[?25h");
+ static::writer()->writeDirectly("\e[?25h");
static::$cursorHidden = false;
}
@@ -58,6 +58,6 @@ public function moveCursor(int $x, int $y = 0): void
$sequence .= "\e[{$y}B"; // Down
}
- static::writeDirectly($sequence);
+ static::writer()->writeDirectly($sequence);
}
}
diff --git a/src/Concerns/Erase.php b/src/Concerns/Erase.php
index 943dba6e..7de49fa2 100644
--- a/src/Concerns/Erase.php
+++ b/src/Concerns/Erase.php
@@ -18,7 +18,7 @@ public function eraseLines(int $count): void
$clear .= "\e[G";
}
- static::writeDirectly($clear);
+ static::writer()->writeDirectly($clear);
}
/**
@@ -26,6 +26,6 @@ public function eraseLines(int $count): void
*/
public function eraseDown(): void
{
- static::writeDirectly("\e[J");
+ static::writer()->writeDirectly("\e[J");
}
}
diff --git a/src/Concerns/FakesInputOutput.php b/src/Concerns/FakesInputOutput.php
index 2df265f1..29745cc8 100644
--- a/src/Concerns/FakesInputOutput.php
+++ b/src/Concerns/FakesInputOutput.php
@@ -74,11 +74,12 @@ public static function assertStrippedOutputDoesntContain(string $string): void
*/
public static function content(): string
{
- if (! static::output() instanceof BufferedConsoleOutput) {
+ $output = static::writer()->getOutput();
+ if (! $output instanceof BufferedConsoleOutput) {
throw new RuntimeException('Prompt must be faked before accessing content.');
}
- return static::output()->content();
+ return $output->content();
}
/**
diff --git a/src/Note.php b/src/Note.php
index b2239673..e01226d7 100644
--- a/src/Note.php
+++ b/src/Note.php
@@ -29,7 +29,7 @@ public function prompt(): bool
$this->state = 'submit';
- static::output()->write($this->renderTheme());
+ static::writer()->write($this->renderTheme());
return true;
}
diff --git a/src/Output/BufferedConsoleOutput.php b/src/Output/BufferedConsoleOutput.php
index b5e725b4..68fdb353 100644
--- a/src/Output/BufferedConsoleOutput.php
+++ b/src/Output/BufferedConsoleOutput.php
@@ -2,6 +2,8 @@
namespace Laravel\Prompts\Output;
+use Symfony\Component\Console\Output\ConsoleOutput;
+
class BufferedConsoleOutput extends ConsoleOutput
{
/**
diff --git a/src/Output/ConsoleOutput.php b/src/Output/PromptWriter.php
similarity index 56%
rename from src/Output/ConsoleOutput.php
rename to src/Output/PromptWriter.php
index 60381d62..23963c0b 100644
--- a/src/Output/ConsoleOutput.php
+++ b/src/Output/PromptWriter.php
@@ -2,15 +2,20 @@
namespace Laravel\Prompts\Output;
-use Symfony\Component\Console\Output\ConsoleOutput as SymfonyConsoleOutput;
+use Symfony\Component\Console\Output\OutputInterface;
-class ConsoleOutput extends SymfonyConsoleOutput
+class PromptWriter
{
/**
* How many new lines were written by the last output.
*/
protected int $newLinesWritten = 1;
+ public function __construct(
+ private OutputInterface $output
+ ) {
+ }
+
/**
* How many new lines were written by the last output.
*/
@@ -22,15 +27,15 @@ public function newLinesWritten(): int
/**
* Write the output and capture the number of trailing new lines.
*/
- protected function doWrite(string $message, bool $newline): void
+ public function write(string $message, bool $newline = false): void
{
- parent::doWrite($message, $newline);
+ $this->output->write($message, $newline);
if ($newline) {
$message .= \PHP_EOL;
}
- $trailingNewLines = strlen($message) - strlen(rtrim($message, \PHP_EOL));
+ $trailingNewLines = \strlen($message) - \strlen(rtrim($message, \PHP_EOL));
if (trim($message) === '') {
$this->newLinesWritten += $trailingNewLines;
@@ -42,8 +47,13 @@ protected function doWrite(string $message, bool $newline): void
/**
* Write output directly, bypassing newline capture.
*/
- public function writeDirectly(string $message): void
+ public function writeDirectly(string $message, bool $newline = false): void
+ {
+ $this->output->write($message, $newline);
+ }
+
+ public function getOutput(): OutputInterface
{
- parent::doWrite($message, false);
+ return $this->output;
}
}
diff --git a/src/Prompt.php b/src/Prompt.php
index 1f50b102..dfd3e872 100644
--- a/src/Prompt.php
+++ b/src/Prompt.php
@@ -3,8 +3,10 @@
namespace Laravel\Prompts;
use Closure;
-use Laravel\Prompts\Output\ConsoleOutput;
+use Laravel\Prompts\Output\PromptWriter;
use RuntimeException;
+use Symfony\Component\Console\Output\ConsoleOutput;
+use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;
@@ -65,9 +67,9 @@ abstract class Prompt
protected static ?Closure $validateUsing;
/**
- * The output instance.
+ * The writer instance.
*/
- protected static OutputInterface $output;
+ protected static PromptWriter $writer;
/**
* The terminal instance.
@@ -91,7 +93,7 @@ public function prompt(): mixed
return $this->fallback();
}
- static::$interactive ??= stream_isatty(STDIN);
+ static::$interactive ??= stream_isatty(STDIN) && (stream_isatty(STDOUT) || stream_isatty(STDERR));
if (! static::$interactive) {
return $this->default();
@@ -102,7 +104,7 @@ public function prompt(): mixed
try {
static::terminal()->setTty('-icanon -isig -echo');
} catch (Throwable $e) {
- static::output()->writeln("{$e->getMessage()}");
+ static::writer()->write("{$e->getMessage()}", true);
static::fallbackWhen(true);
return $this->fallback();
@@ -154,9 +156,7 @@ public function newLinesWritten(): int
*/
protected function capturePreviousNewLines(): void
{
- $this->newLinesWritten = method_exists(static::output(), 'newLinesWritten')
- ? static::output()->newLinesWritten()
- : 1;
+ $this->newLinesWritten = static::writer()->newLinesWritten();
}
/**
@@ -164,27 +164,22 @@ protected function capturePreviousNewLines(): void
*/
public static function setOutput(OutputInterface $output): void
{
- self::$output = $output;
+ if ($output instanceof ConsoleOutputInterface && stream_isatty(STDERR) && ! stream_isatty(STDOUT)) {
+ $output = $output->getErrorOutput();
+ }
+ self::$writer = new PromptWriter($output);
}
/**
- * Get the current output instance.
+ * Get the prompt writer.
*/
- protected static function output(): OutputInterface
+ protected static function writer(): PromptWriter
{
- return self::$output ??= new ConsoleOutput();
- }
+ if (! isset(self::$writer)) {
+ self::setOutput(new ConsoleOutput());
+ }
- /**
- * Write output directly, bypassing newline capture.
- */
- protected static function writeDirectly(string $message): void
- {
- match (true) {
- method_exists(static::output(), 'writeDirectly') => static::output()->writeDirectly($message),
- method_exists(static::output(), 'getOutput') => static::output()->getOutput()->write($message),
- default => static::output()->write($message),
- };
+ return self::$writer;
}
/**
@@ -215,7 +210,7 @@ protected function render(): void
}
if ($this->state === 'initial') {
- static::output()->write($frame);
+ static::writer()->write($frame);
$this->state = 'active';
$this->prevFrame = $frame;
@@ -228,7 +223,7 @@ protected function render(): void
// Ensure that the full frame is buffered so subsequent output can see how many trailing newlines were written.
if ($this->state === 'submit') {
$this->eraseDown();
- static::output()->write($frame);
+ static::writer()->write($frame);
$this->prevFrame = '';
@@ -242,7 +237,7 @@ protected function render(): void
$this->moveCursor(0, $diffLine);
$this->eraseLines(1);
$lines = explode(PHP_EOL, $frame);
- static::output()->write($lines[$diffLine]);
+ static::writer()->write($lines[$diffLine]);
$this->moveCursor(0, count($lines) - $diffLine - 1);
} elseif (count($diff) > 1) { // Re-render everything past the first change
$diffLine = $diff[0];
@@ -250,7 +245,7 @@ protected function render(): void
$this->eraseDown();
$lines = explode(PHP_EOL, $frame);
$newLines = array_slice($lines, $diffLine);
- static::output()->write(implode(PHP_EOL, $newLines));
+ static::writer()->write(implode(PHP_EOL, $newLines));
}
$this->prevFrame = $frame;
diff --git a/src/Table.php b/src/Table.php
index a7de3700..40b6538a 100644
--- a/src/Table.php
+++ b/src/Table.php
@@ -56,7 +56,7 @@ public function prompt(): bool
$this->state = 'submit';
- static::output()->write($this->renderTheme());
+ static::writer()->write($this->renderTheme());
return true;
}