> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel Prompts

> 說明如何使用 laravel/prompts 套件為 Artisan 指令加入精美的互動式 UI。介紹文字輸入、選擇、搜尋、autocomplete、任務日誌、串流輸出等多樣函式。

## 前言

[Laravel Prompts](https://github.com/laravel/prompts) 是為命令列應用程式加入美觀好用的互動式表單的 PHP 套件。
提供類似瀏覽器表單的體驗，如佔位文字、驗證等。

由於可直接在 Artisan 指令的程式碼中呼叫，能簡潔而直覺地寫出對使用者的問答。

<Info>
  Laravel Prompts 支援 macOS、Linux、Windows（WSL）。
  在不支援的環境會自動切換為 fallback 行為。
</Info>

## 安裝

Laravel Prompts 已隨 Laravel 本體一併提供，無需另行安裝。

若要在其他 PHP 專案使用，可透過 Composer 安裝：

```shell theme={null}
composer require laravel/prompts
```

## 基本 prompt 函式

### text — 文字輸入

以 `text()` 要求使用者輸入字串。

```php theme={null}
use function Laravel\Prompts\text;

$name = text('What is your name?');
```

可設定佔位文字、預設值、提示：

```php theme={null}
$name = text(
    label: 'What is your name?',
    placeholder: 'E.g. Taylor Otwell',
    default: $user?->name,
    hint: 'This will be displayed on your profile.'
);
```

指定 `required` 可將輸入設為必填。也可自訂驗證訊息。

```php theme={null}
$name = text(
    label: 'What is your name?',
    required: 'Your name is required.'
);
```

可用 `validate` closure 追加驗證。回傳錯誤訊息，或於合格時回傳 `null`。

```php theme={null}
$name = text(
    label: 'What is your name?',
    validate: fn (string $value) => match (true) {
        strlen($value) < 3 => 'The name must be at least 3 characters.',
        strlen($value) > 255 => 'The name must not exceed 255 characters.',
        default => null
    }
);
```

也可以以陣列形式指定 Laravel 的驗證規則。

```php theme={null}
$name = text(
    label: 'What is your name?',
    validate: ['name' => 'required|max:255|unique:users']
);
```

### textarea — 多行文字輸入

`textarea()` 接受多行輸入。

```php theme={null}
use function Laravel\Prompts\textarea;

$story = textarea('Tell me a story.');
```

### number — 數字輸入

`number()` 接受數字。可用上下方向鍵增減值。

```php theme={null}
use function Laravel\Prompts\number;

$copies = number(
    label: 'How many copies would you like?',
    default: 1,
    validate: ['copies' => 'required|integer|min:1|max:100']
);
```

### password — 密碼輸入

`password()` 與文字輸入類似，但輸入內容不會顯示在畫面上。

```php theme={null}
use function Laravel\Prompts\password;

$password = password('What is your password?');
```

```php theme={null}
$password = password(
    label: 'What is your password?',
    placeholder: 'password',
    hint: 'Minimum 8 characters.',
    validate: fn (string $value) => strlen($value) < 8
        ? 'The password must be at least 8 characters.'
        : null
);
```

### confirm — Yes/No 確認

`confirm()` 讓使用者做二擇一的確認。回傳 `true` 或 `false`。

```php theme={null}
use function Laravel\Prompts\confirm;

$confirmed = confirm('Do you accept the terms?');
```

可自訂預設值與標籤文字。

```php theme={null}
$confirmed = confirm(
    label: 'Do you accept the terms?',
    default: false,
    yes: 'I accept',
    no: 'I decline',
    hint: 'The terms must be accepted to continue.'
);
```

### select — 選擇清單

`select()` 讓使用者從清單中選一個。

```php theme={null}
use function Laravel\Prompts\select;

$role = select(
    label: 'What role should the user have?',
    options: ['Member', 'Contributor', 'Owner'],
    default: 'Owner'
);
```

使用關聯陣列時，回傳值會是 key 而非顯示的 label。

```php theme={null}
$role = select(
    label: 'What role should the user have?',
    options: [
        'member'      => 'Member',
        'contributor' => 'Contributor',
        'owner'       => 'Owner',
    ],
    default: 'owner'
);
```

可用 `scroll` 變更卷動前顯示的選項數量（預設 5 個）。

```php theme={null}
$role = select(
    label: 'Which category would you like to assign?',
    options: Category::pluck('name', 'id'),
    scroll: 10
);
```

### multiselect — 複選

`multiselect()` 讓使用者同時選多個選項。

```php theme={null}
use function Laravel\Prompts\multiselect;

$permissions = multiselect(
    label: 'What permissions should be assigned?',
    options: ['Read', 'Create', 'Update', 'Delete']
);
```

指定 `required` 可要求至少選一個。

```php theme={null}
$permissions = multiselect(
    label: 'What permissions should be assigned?',
    options: ['Read', 'Create', 'Update', 'Delete'],
    required: 'At least one permission must be selected.'
);
```

### suggest — 附自動完成的輸入

`suggest()` 在提供候選的同時也接受自由輸入。

```php theme={null}
use function Laravel\Prompts\suggest;

$name = suggest(
    label: 'What is your name?',
    options: ['Taylor', 'Dayle']
);
```

傳入 closure 可依輸入內容動態篩選候選。

```php theme={null}
$name = suggest(
    label: 'What is your name?',
    options: fn (string $value) => collect(['Taylor', 'Tobi', 'Dries'])
        ->filter(fn ($name) => str_starts_with($name, $value))
        ->values()
        ->all()
);
```

### search — 動態搜尋

`search()` 會在每次輸入時更新候選清單。closure 回傳的陣列即為候選。

```php theme={null}
use function Laravel\Prompts\search;

$userId = search(
    label: 'Search for the user that should receive the mail',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : []
);
```

### multisearch — 動態複選

`multisearch()` 可透過動態搜尋進行複選。

```php theme={null}
use function Laravel\Prompts\multisearch;

$userIds = multisearch(
    label: 'Search for the users that should receive the mail',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : []
);
```

### pause — 暫停

用 `pause()` 提示使用者按下 Enter 鍵，暫停處理流程。

```php theme={null}
use function Laravel\Prompts\pause;

pause('Press ENTER to continue.');
```

### autocomplete — 行內補全

`autocomplete()` 是以 ghost text 顯示候選的行內補全函式。與 `suggest()` 不同，使用者每次輸入時符合的候選會以 ghost text 顯示，按 `Tab` 或右方向鍵即可確定補全。

```php theme={null}
use function Laravel\Prompts\autocomplete;

$name = autocomplete(
    label: 'What is your name?',
    options: ['Taylor', 'Dayle', 'Jess', 'Nuno', 'Tim']
);
```

可設定佔位文字、預設值、提示。

```php theme={null}
$name = autocomplete(
    label: 'What is your name?',
    options: ['Taylor', 'Dayle', 'Jess', 'Nuno', 'Tim'],
    placeholder: 'E.g. Taylor',
    default: $user?->name,
    hint: 'Use Tab to accept, up/down to cycle.'
);
```

傳入 closure 可依輸入內容動態產生候選。

```php theme={null}
$file = autocomplete(
    label: 'Which file?',
    options: fn (string $value) => collect($files)
        ->filter(fn ($file) => str_starts_with(strtolower($file), strtolower($value)))
        ->values()
        ->all(),
);
```

## 驗證

所有 prompt 函式皆可透過 `validate` 引數設定驗證。

```php theme={null}
$name = text(
    label: 'What is your name?',
    validate: fn (string $value) => match (true) {
        strlen($value) < 3  => 'The name must be at least 3 characters.',
        strlen($value) > 255 => 'The name must not exceed 255 characters.',
        default => null
    }
);
```

若 closure 回傳字串會顯示為錯誤，提示重新輸入。回傳 `null` 表示驗證通過。

也可以陣列形式使用 Laravel 的驗證規則。

```php theme={null}
$name = text(
    label: 'What is your name?',
    validate: ['name' => 'required|max:255|unique:users']
);
```

要在驗證前轉換輸入，可使用 `transform` 引數。

```php theme={null}
$name = text(
    label: 'What is your name?',
    transform: fn (string $value) => trim($value),
    validate: fn (string $value) => match (true) {
        strlen($value) === 0 => 'The name must not be empty.',
        default => null
    }
);
```

## Form

使用 `form()` 可將多個 prompt 合為一組，在完成前可整批取消。

```php theme={null}
use function Laravel\Prompts\form;

$responses = form()
    ->text('What is your name?', required: true, name: 'name')
    ->password('What is your password?', validate: ['password' => 'min:8'], name: 'password')
    ->confirm('Do you accept the terms?')
    ->submit();

$name     = $responses['name'];
$password = $responses['password'];
$confirmed = $responses[2];
```

## 資訊輸出

提供帶樣式輸出文字訊息的函式。

```php theme={null}
use function Laravel\Prompts\info;
use function Laravel\Prompts\warning;
use function Laravel\Prompts\error;
use function Laravel\Prompts\alert;
use function Laravel\Prompts\note;

note('Prepare for launch.');
info('User created successfully.');
warning('This action cannot be undone.');
error('Something went wrong.');
alert('Critical failure detected!');
```

## Callout

`callout()` 會將標籤與內容以框線顯示。適合突出顯示部署摘要、錯誤詳情、狀態更新等重要資訊。

```php theme={null}
use function Laravel\Prompts\callout;

callout(
    label: 'Environment Configured',
    content: 'Your application is running in production mode with 4 workers.',
);
```

在 `type` 引數指定 `'warning'` 或 `'error'`，可變更視覺樣式。

```php theme={null}
callout(
    label: 'Deprecation Notice',
    content: 'The `--prefer-stable` flag will be removed in v4.0.',
    type: 'warning',
);

callout(
    label: 'Database Connection Failed',
    content: 'Could not connect to MySQL on 127.0.0.1:3306.',
    type: 'error',
);
```

可用 `info` 引數加入頁尾行。便於顯示 ID 或時間戳等中繼資料。

```php theme={null}
callout(
    label: 'Deployment Summary',
    content: 'Your application was deployed to production.',
    info: 'deploy-id: d4f8a2c',
);
```

### 豐富內容

以陣列取代字串，可製作結構化的豐富 callout。`Element` 類別提供建立標題、項目符號清單、編號清單、鍵值清單與連結的 factory 方法。

```php theme={null}
use Laravel\Prompts\Elements\Element;
use function Laravel\Prompts\callout;

callout('Deployment Summary', [
    'Your application was deployed to production at 2024-03-15 14:32 UTC.',
    Element::heading('What Changed'),
    Element::bulletedList([
        'Migrated 3 pending database migrations',
        'Cleared and rebuilt route cache',
        'Restarted 4 queue workers',
    ]),
    Element::heading('Next Steps'),
    Element::numberedList([
        'Verify the health check endpoint at /up',
        'Monitor error rates for the next 15 minutes',
        'Confirm background jobs are processing',
    ]),
]);
```

以 `Element::keyValueList` 顯示附標籤的資料。

```php theme={null}
callout('Database Connection Failed', [
    'Could not connect to the database server.',
    Element::keyValueList([
        'Host'     => '127.0.0.1',
        'Port'     => '3306',
        'Database' => 'forge',
        'Status'   => 'Connection refused',
    ]),
], type: 'error');
```

`Element::link` 會在支援 [OSC 8](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda) 的終端機中產生可點擊的超連結。可只傳 URL，或傳 URL 加自訂 label。

```php theme={null}
callout('Server Health Check', [
    'Multiple services are reporting degraded performance.',
    Element::heading('Affected Services'),
    'Look here: '.Element::link('https://example.com/health', 'Health Dashboard'),
    Element::link('https://example.com/health'),
]);
```

若省略 label，URL 會作為連結文字顯示。

## 表格顯示

`table()` 可以表格形式顯示資料。

```php theme={null}
use function Laravel\Prompts\table;

table(
    headers: ['Name', 'Email'],
    rows: User::all(['name', 'email'])->toArray()
);
```

## Spin（載入顯示）

`spin()` 會在 closure 執行期間顯示載入指示器。

```php theme={null}
use function Laravel\Prompts\spin;

$response = spin(
    message: 'Fetching response...',
    callback: fn () => Http::get('http://example.com')
);
```

<Warning>
  使用 `spin()` 需要 `pcntl` PHP 擴充功能。在無法使用的環境中不會顯示 spinner。
</Warning>

## 進度條

`progress()` 可視覺化顯示反覆處理的進度。

```php theme={null}
use function Laravel\Prompts\progress;

$users = progress(
    label: 'Updating users',
    steps: User::all(),
    callback: fn ($user) => $this->performTask($user),
    hint: 'This may take some time.'
);
```

也可以手動控制進度條。

```php theme={null}
$progress = progress(label: 'Uploading files', steps: count($files));

$progress->start();

foreach ($files as $file) {
    $this->uploadFile($file);
    $progress->advance();
}

$progress->finish();
```

## Task

`task()` 會在 callback 執行期間顯示 spinner 與可捲動的即時輸出區域。非常適合包裝依賴安裝或部署腳本等長時間執行的處理，可即時看到發生的事。

```php theme={null}
use function Laravel\Prompts\task;

task(
    label: 'Installing dependencies',
    callback: function ($logger) {
        // 長時間執行的處理...
    }
);
```

callback 會收到 `Logger` 實例，可即時顯示日誌行或狀態訊息。

<Warning>
  使用 `task()` 需要 `pcntl` PHP 擴充功能。在無法使用的環境中會 fallback 為靜態顯示。
</Warning>

### 輸出日誌行

以 `line` 方法逐行寫入捲動輸出區。

```php theme={null}
task(
    label: 'Installing dependencies',
    callback: function ($logger) {
        $logger->line('Resolving packages...');
        $logger->line('Downloading laravel/framework');
    }
);
```

### 狀態訊息

使用 `success`、`warning`、`error` 可在捲動日誌區的上方顯示固定的高亮訊息。

```php theme={null}
task(
    label: 'Deploying application',
    callback: function ($logger) {
        $logger->line('Pulling latest changes...');
        $logger->success('Changes pulled!');

        $logger->line('Running migrations...');
        $logger->warning('No new migrations to run.');

        $logger->line('Clearing cache...');
        $logger->success('Cache cleared!');
    }
);
```

### 更新標籤

以 `label` 方法可在執行中更新任務標籤。`subLabel` 方法會設定顯示在標籤下方的淡色 sub label。傳入空字串可清除 sub label。也能用 `subLabel` 引數指定初始 sub label。

```php theme={null}
task(
    label: 'Deploying',
    callback: function ($logger) {
        $logger->subLabel('Building assets...');
        // ...
        $logger->subLabel('Running migrations...');
        // ...
        $logger->subLabel('');
    },
    subLabel: 'Preparing...'
);
```

### 文字串流

對於像 AI 生成回應那樣階段性產生的輸出，可用 `partial` 方法逐個串流文字。串流完成後呼叫 `commitPartial` 進行確定。

```php theme={null}
task(
    label: 'Generating response...',
    callback: function ($logger) {
        foreach ($words as $word) {
            $logger->partial($word . ' ');
        }

        $logger->commitPartial();
    }
);
```

### 輸出上限與 summary 的保留

預設最多顯示 10 行捲動輸出。可用 `limit` 引數自訂。若要在任務完成後仍將狀態訊息保留在畫面上，可傳入 `keepSummary: true`。

```php theme={null}
task(
    label: 'Deploying',
    callback: function ($logger) {
        $logger->success('Assets built');
        $logger->success('Migrations complete');
    },
    limit: 20,
    keepSummary: true,
);
```

## Stream

`stream()` 會將文字階段性顯示到終端機。適合顯示 AI 生成內容或以 chunk 形式抵達的資料。

```php theme={null}
use function Laravel\Prompts\stream;

$stream = stream();

foreach ($words as $word) {
    $stream->append($word . ' ');
    usleep(25_000); // 模擬 chunk 間的延遲...
}

$stream->close();
```

`append` 方法會以 fade-in 效果將文字加入 stream。所有內容 stream 完後呼叫 `close`，即可確定輸出並復原游標。

## 終端機操作

### 設定終端機標題

```php theme={null}
use function Laravel\Prompts\title;

title('My Application');
```

傳入空字串可重設為預設標題。

```php theme={null}
title('');
```

### 清除終端機

```php theme={null}
use function Laravel\Prompts\clear;

clear();
```

## 終端機相關考量

**終端機寬度**：當標籤、選項、驗證訊息超過終端機欄數時，會自動被截斷。若以 80 欄為前提，建議控制在 74 字元以內。

**終端機高度**：接受 `scroll` 引數的 prompt 會自動調整值，使其含驗證訊息空間也能容納於終端機高度內。

## Fallback

在不支援的環境（如 Windows non-WSL）會自動 fallback。
預設會改用 Laravel 的 `$this->ask()`、`$this->choice()` 等內建方法。

## 測試

Laravel Prompts 可與 Pest 或 PHPUnit 的測試整合。

```php theme={null}
use Laravel\Prompts\Prompt;

Prompt::fake(['Taylor', true]);

$name      = text('What is your name?');
$confirmed = confirm('Do you accept the terms?');

Prompt::assertOutputContains('What is your name?');
```

使用 Laravel 的 Artisan 測試 helper，也能對資訊輸出函式撰寫斷言。

```php theme={null}
// Pest
test('report generation', function () {
    $this->artisan('report:generate')
        ->expectsPromptsInfo('Welcome to the application!')
        ->expectsPromptsWarning('This action cannot be undone')
        ->expectsPromptsError('Something went wrong')
        ->expectsPromptsAlert('Important notice!')
        ->expectsPromptsTable(
            headers: ['Name', 'Email'],
            rows: [
                ['Taylor Otwell', 'taylor@example.com'],
            ]
        )
        ->assertExitCode(0);
});
```

```php theme={null}
// PHPUnit
public function test_report_generation(): void
{
    $this->artisan('report:generate')
        ->expectsPromptsInfo('Welcome to the application!')
        ->expectsPromptsWarning('This action cannot be undone')
        ->expectsPromptsTable(
            headers: ['Name', 'Email'],
            rows: [['Taylor Otwell', 'taylor@example.com']]
        )
        ->assertExitCode(0);
}
```

## 相關頁面

<Columns cols={2}>
  <Card title="Artisan Console" icon="terminal" href="/zh-TW/artisan">
    在 Artisan 指令中活用 Prompts
  </Card>
</Columns>


## Related topics

- [Laravel Chisel — 啟動套件的安裝後腳本函式庫](/zh-TW/blog/chisel-introduction.md)
- [GitHub Copilot SDK for Laravel](/zh-TW/packages/laravel-copilot-sdk/index.md)
- [Streaming](/zh-TW/packages/laravel-copilot-sdk/streaming.md)
- [主控台測試](/zh-TW/console-tests.md)
- [權限請求](/zh-TW/packages/laravel-copilot-sdk/permission-request.md)
