or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application.mdcommands.mdexceptions.mdindex.mdio.mdstyling.mdtesting.mdui.md

ui.mddocs/

0

# User Interface Components

1

2

The UI components system provides rich interactive elements for building sophisticated CLI applications. It includes tables for data display, progress indicators for long-running operations, and question prompts for user interaction.

3

4

## Capabilities

5

6

### Table System

7

8

Comprehensive table rendering with customizable styling, headers, rows, and formatting options.

9

10

```python { .api }

11

class Table:

12

def __init__(self, io: IO | Output, style: str | None = None) -> None:

13

"""

14

Create a table for output.

15

16

Args:

17

io (IO | Output): Output destination

18

style (str | None): Table style name

19

"""

20

21

def set_headers(self, headers: list[str]) -> Table:

22

"""

23

Set table headers.

24

25

Args:

26

headers (list[str]): Header row content

27

28

Returns:

29

Table: Self for method chaining

30

"""

31

32

def set_rows(self, rows: Rows) -> Table:

33

"""

34

Set table rows.

35

36

Args:

37

rows (Rows): Table row data

38

39

Returns:

40

Table: Self for method chaining

41

"""

42

43

def add_rows(self, rows: Rows) -> Table:

44

"""

45

Add rows to existing table data.

46

47

Args:

48

rows (Rows): Additional rows to add

49

50

Returns:

51

Table: Self for method chaining

52

"""

53

54

def add_row(self, row: list[str] | TableSeparator) -> Table:

55

"""

56

Add a single row.

57

58

Args:

59

row (list[str] | TableSeparator): Row data or separator

60

61

Returns:

62

Table: Self for method chaining

63

"""

64

65

def set_style(self, name: str) -> Table:

66

"""

67

Set table style by name.

68

69

Args:

70

name (str): Style name (compact, borderless, box, etc.)

71

72

Returns:

73

Table: Self for method chaining

74

"""

75

76

def get_style(self) -> TableStyle:

77

"""

78

Get current table style.

79

80

Returns:

81

TableStyle: Current style configuration

82

"""

83

84

def set_column_style(self, column_index: int, name: str) -> Table:

85

"""

86

Set style for a specific column.

87

88

Args:

89

column_index (int): Column index (0-based)

90

name (str): Style name

91

92

Returns:

93

Table: Self for method chaining

94

"""

95

96

def render(self) -> None:

97

"""Render the table to output."""

98

```

99

100

### Table Styling Components

101

102

Components for customizing table appearance and formatting.

103

104

```python { .api }

105

class TableStyle:

106

def __init__(self) -> None: ...

107

108

def set_horizontal_border_char(self, char: str) -> TableStyle: ...

109

def set_vertical_border_char(self, char: str) -> TableStyle: ...

110

def set_crossing_char(self, char: str) -> TableStyle: ...

111

def set_cell_header_format(self, format: str) -> TableStyle: ...

112

def set_cell_row_format(self, format: str) -> TableStyle: ...

113

def set_border_format(self, format: str) -> TableStyle: ...

114

def set_pad_type(self, pad_type: int) -> TableStyle: ...

115

116

class TableSeparator:

117

"""Represents a separator row in tables."""

118

pass

119

120

class TableCell:

121

def __init__(self, value: str = "", colspan: int = 1, style: TableCellStyle | None = None) -> None:

122

"""

123

Create a table cell with optional spanning and styling.

124

125

Args:

126

value (str): Cell content

127

colspan (int): Number of columns to span

128

style (TableCellStyle | None): Cell-specific styling

129

"""

130

131

class TableCellStyle:

132

def __init__(self, fg: str | None = None, bg: str | None = None,

133

options: list[str] | None = None, align: str = "left",

134

cell_format: str | None = None) -> None:

135

"""

136

Create cell-specific styling.

137

138

Args:

139

fg (str | None): Foreground color

140

bg (str | None): Background color

141

options (list[str] | None): Formatting options

142

align (str): Text alignment (left, center, right)

143

cell_format (str | None): Cell format template

144

"""

145

```

146

147

### Progress Indicators

148

149

Visual indicators for long-running operations with customizable formatting and progress tracking.

150

151

```python { .api }

152

class ProgressBar:

153

def __init__(self, io: IO | Output, max: int = 0) -> None:

154

"""

155

Create a progress bar.

156

157

Args:

158

io (IO | Output): Output destination

159

max (int): Maximum progress value

160

"""

161

162

def start(self, max: int | None = None) -> None:

163

"""

164

Start the progress bar.

165

166

Args:

167

max (int | None): Optional maximum value override

168

"""

169

170

def advance(self, step: int = 1) -> None:

171

"""

172

Advance progress by steps.

173

174

Args:

175

step (int): Number of steps to advance

176

"""

177

178

def set_progress(self, progress: int) -> None:

179

"""

180

Set progress to specific value.

181

182

Args:

183

progress (int): Current progress value

184

"""

185

186

def finish(self) -> None:

187

"""Finish and hide the progress bar."""

188

189

def clear(self) -> None:

190

"""Clear the progress bar from output."""

191

192

def display(self) -> None:

193

"""Display/refresh the progress bar."""

194

195

def set_format(self, format: str) -> None:

196

"""

197

Set progress bar format.

198

199

Args:

200

format (str): Format string for display

201

"""

202

203

def set_message(self, message: str, name: str = "message") -> None:

204

"""

205

Set a message to display with progress.

206

207

Args:

208

message (str): Message content

209

name (str): Message name/key

210

"""

211

212

def get_message(self, name: str = "message") -> str:

213

"""

214

Get a message by name.

215

216

Args:

217

name (str): Message name/key

218

219

Returns:

220

str: Message content

221

"""

222

223

@property

224

def progress(self) -> int:

225

"""Get current progress value."""

226

227

@property

228

def max_steps(self) -> int:

229

"""Get maximum steps."""

230

231

@property

232

def percent(self) -> float:

233

"""Get completion percentage."""

234

235

class ProgressIndicator:

236

def __init__(self, io: IO | Output, format: str | None = None,

237

indicator_change_interval: int = 100) -> None:

238

"""

239

Create a spinning progress indicator.

240

241

Args:

242

io (IO | Output): Output destination

243

format (str | None): Display format

244

indicator_change_interval (int): Milliseconds between indicator changes

245

"""

246

247

def start(self, message: str) -> None:

248

"""

249

Start the indicator with message.

250

251

Args:

252

message (str): Initial message to display

253

"""

254

255

def advance(self) -> None:

256

"""Advance the indicator animation."""

257

258

def finish(self, message: str, reset_indicator: bool = False) -> None:

259

"""

260

Finish the indicator with final message.

261

262

Args:

263

message (str): Final message

264

reset_indicator (bool): Whether to reset animation

265

"""

266

267

def set_message(self, message: str) -> None:

268

"""

269

Update the indicator message.

270

271

Args:

272

message (str): New message

273

"""

274

```

275

276

### Question System

277

278

Interactive prompts for gathering user input with validation and formatting options.

279

280

```python { .api }

281

class Question:

282

def __init__(self, question: str, default: Any = None) -> None:

283

"""

284

Create a question prompt.

285

286

Args:

287

question (str): Question text to display

288

default (Any): Default answer if user provides no input

289

"""

290

291

def ask(self, io: IO) -> Any:

292

"""

293

Ask the question and get user response.

294

295

Args:

296

io (IO): IO interface for interaction

297

298

Returns:

299

Any: User's answer or default value

300

"""

301

302

def hide(self, hidden: bool = True) -> None:

303

"""

304

Set whether input should be hidden (password input).

305

306

Args:

307

hidden (bool): Whether to hide input

308

"""

309

310

def set_hidden(self, hidden: bool) -> Question:

311

"""

312

Set hidden input and return self for chaining.

313

314

Args:

315

hidden (bool): Whether to hide input

316

317

Returns:

318

Question: Self for method chaining

319

"""

320

321

def set_validator(self, validator: Callable[[Any], Any]) -> Question:

322

"""

323

Set input validator function.

324

325

Args:

326

validator (Callable): Function to validate input

327

328

Returns:

329

Question: Self for method chaining

330

"""

331

332

def set_max_attempts(self, max_attempts: int) -> Question:

333

"""

334

Set maximum validation attempts.

335

336

Args:

337

max_attempts (int): Maximum number of attempts

338

339

Returns:

340

Question: Self for method chaining

341

"""

342

343

def set_normalizer(self, normalizer: Callable[[str], str]) -> Question:

344

"""

345

Set input normalizer function.

346

347

Args:

348

normalizer (Callable): Function to normalize input

349

350

Returns:

351

Question: Self for method chaining

352

"""

353

354

class ChoiceQuestion(Question):

355

def __init__(self, question: str, choices: list[str], default: Any = None) -> None:

356

"""

357

Create a multiple choice question.

358

359

Args:

360

question (str): Question text

361

choices (list[str]): Available choices

362

default (Any): Default choice

363

"""

364

365

def set_multiselect(self, multiselect: bool) -> ChoiceQuestion:

366

"""

367

Allow multiple choice selection.

368

369

Args:

370

multiselect (bool): Whether to allow multiple selections

371

372

Returns:

373

ChoiceQuestion: Self for method chaining

374

"""

375

376

def set_error_message(self, message: str) -> ChoiceQuestion:

377

"""

378

Set error message for invalid choices.

379

380

Args:

381

message (str): Error message template

382

383

Returns:

384

ChoiceQuestion: Self for method chaining

385

"""

386

387

class ConfirmationQuestion(Question):

388

def __init__(self, question: str, default: bool = True) -> None:

389

"""

390

Create a yes/no confirmation question.

391

392

Args:

393

question (str): Question text

394

default (bool): Default answer (True for yes, False for no)

395

"""

396

```

397

398

### UI Coordinator

399

400

Main UI component that provides access to all interactive elements from commands.

401

402

```python { .api }

403

class UI:

404

def __init__(self, io: IO) -> None:

405

"""

406

Create UI coordinator.

407

408

Args:

409

io (IO): IO interface for interaction

410

"""

411

412

def table(self, headers: list[str] | None = None, rows: Rows | None = None) -> Table:

413

"""

414

Create a table.

415

416

Args:

417

headers (list[str] | None): Optional table headers

418

rows (Rows | None): Optional table rows

419

420

Returns:

421

Table: Configured table instance

422

"""

423

424

def progress_bar(self, max: int = 0) -> ProgressBar:

425

"""

426

Create a progress bar.

427

428

Args:

429

max (int): Maximum progress value

430

431

Returns:

432

ProgressBar: Progress bar instance

433

"""

434

435

def progress_indicator(self) -> ProgressIndicator:

436

"""

437

Create a progress indicator.

438

439

Returns:

440

ProgressIndicator: Spinning indicator instance

441

"""

442

443

def ask(self, question: str | Question, default: Any | None = None) -> Any:

444

"""

445

Ask a question.

446

447

Args:

448

question (str | Question): Question text or Question object

449

default (Any | None): Default answer

450

451

Returns:

452

Any: User's answer

453

"""

454

455

def confirm(self, question: str, default: bool = True) -> bool:

456

"""

457

Ask a confirmation question.

458

459

Args:

460

question (str): Question text

461

default (bool): Default answer

462

463

Returns:

464

bool: User's confirmation

465

"""

466

467

def choice(self, question: str, choices: list[str], default: Any | None = None) -> Any:

468

"""

469

Ask a multiple choice question.

470

471

Args:

472

question (str): Question text

473

choices (list[str]): Available choices

474

default (Any | None): Default choice

475

476

Returns:

477

Any: Selected choice

478

"""

479

```

480

481

## Type Definitions

482

483

```python { .api }

484

# Type alias for table row data

485

Rows = list[list[str] | TableSeparator]

486

```

487

488

## Usage Examples

489

490

### Basic Table Display

491

492

```python

493

from cleo.ui.table import Table

494

495

class ReportCommand(Command):

496

def handle(self):

497

# Create and configure table

498

table = self.table()

499

table.set_headers(['Name', 'Status', 'Count'])

500

table.set_rows([

501

['Service A', 'Running', '150'],

502

['Service B', 'Stopped', '0'],

503

['Service C', 'Warning', '75']

504

])

505

table.render()

506

507

# Alternative: use table() helper

508

table = self.table(

509

headers=['ID', 'Task', 'Progress'],

510

rows=[

511

['001', 'Data Processing', '85%'],

512

['002', 'File Upload', '100%'],

513

['003', 'Validation', '45%']

514

]

515

)

516

table.render()

517

```

518

519

### Advanced Table Formatting

520

521

```python

522

class StatusCommand(Command):

523

def handle(self):

524

table = self.table()

525

526

# Styled headers

527

table.set_headers([

528

'<info>Service</info>',

529

'<comment>Status</comment>',

530

'<question>Uptime</question>'

531

])

532

533

# Mixed content with separators

534

table.add_row(['Web Server', '<info>✓ Running</info>', '5d 3h'])

535

table.add_row(['Database', '<error>✗ Stopped</error>', '0m'])

536

table.add_row(TableSeparator()) # Add separator line

537

table.add_row(['Cache', '<comment>⚠ Warning</comment>', '2h 15m'])

538

539

# Custom table style

540

table.set_style('box')

541

table.render()

542

543

# Column-specific styling

544

performance_table = self.table()

545

performance_table.set_column_style(2, 'right') # Right-align third column

546

performance_table.set_headers(['Metric', 'Value', 'Unit'])

547

performance_table.add_rows([

548

['CPU Usage', '45.2', '%'],

549

['Memory', '2.1', 'GB'],

550

['Disk I/O', '123.4', 'MB/s']

551

])

552

performance_table.render()

553

```

554

555

### Progress Bar Usage

556

557

```python

558

class ProcessCommand(Command):

559

def handle(self):

560

items = self.get_items_to_process()

561

562

# Create progress bar

563

progress = self.progress_bar(len(items))

564

progress.start()

565

566

for i, item in enumerate(items):

567

# Process item

568

self.process_item(item)

569

570

# Update progress with custom message

571

progress.set_message(f"Processing {item.name}")

572

progress.advance()

573

574

time.sleep(0.1) # Simulate work

575

576

progress.finish()

577

self.line('<info>Processing complete!</info>')

578

579

class DownloadCommand(Command):

580

def handle(self):

581

# Progress bar with custom format

582

progress = self.progress_bar()

583

progress.set_format('Downloading: %current%/%max% [%bar%] %percent:3s%% %message%')

584

585

files = ['file1.txt', 'file2.txt', 'file3.txt']

586

progress.start(len(files))

587

588

for file in files:

589

progress.set_message(f'Current: {file}')

590

# Simulate download

591

time.sleep(1)

592

progress.advance()

593

594

progress.finish()

595

```

596

597

### Progress Indicator for Indefinite Tasks

598

599

```python

600

class SyncCommand(Command):

601

def handle(self):

602

indicator = self.progress_indicator()

603

indicator.start('Synchronizing data...')

604

605

# Perform indefinite task

606

while self.sync_in_progress():

607

indicator.advance()

608

time.sleep(0.1)

609

610

indicator.finish('Synchronization complete!')

611

```

612

613

### Interactive Questions

614

615

```python

616

class SetupCommand(Command):

617

def handle(self):

618

# Simple text input

619

name = self.ask('Project name')

620

621

# Input with default value

622

port = self.ask('Port number', 8080)

623

624

# Hidden input (password)

625

password = self.secret('Database password')

626

627

# Confirmation

628

confirmed = self.confirm('Create project?', True)

629

if not confirmed:

630

self.line('<comment>Setup cancelled</comment>')

631

return 1

632

633

# Multiple choice

634

environment = self.choice(

635

'Environment',

636

['development', 'staging', 'production'],

637

'development'

638

)

639

640

# Multiple selection

641

features = self.choice(

642

'Select features (comma-separated)',

643

['authentication', 'caching', 'logging', 'monitoring'],

644

multiselect=True

645

)

646

647

self.line(f'<info>Creating {name} project for {environment}</info>')

648

return 0

649

```

650

651

### Advanced Question Validation

652

653

```python

654

class ConfigCommand(Command):

655

def handle(self):

656

# Question with validation

657

from cleo.ui.question import Question

658

659

# Email validation

660

email_question = Question('Email address')

661

email_question.set_validator(self.validate_email)

662

email_question.set_max_attempts(3)

663

email = self.ask(email_question)

664

665

# Port number validation

666

port_question = Question('Port (1024-65535)', 8080)

667

port_question.set_validator(lambda x: self.validate_port(int(x)))

668

port_question.set_normalizer(lambda x: x.strip())

669

port = self.ask(port_question)

670

671

def validate_email(self, email):

672

import re

673

if not re.match(r'^[^@]+@[^@]+\.[^@]+$', email):

674

raise ValueError('Invalid email format')

675

return email

676

677

def validate_port(self, port):

678

if not 1024 <= port <= 65535:

679

raise ValueError('Port must be between 1024 and 65535')

680

return port

681

```

682

683

### Combined UI Elements

684

685

```python

686

class DeployCommand(Command):

687

def handle(self):

688

# Confirmation before starting

689

if not self.confirm('Deploy to production?', False):

690

return 1

691

692

# Show deployment steps in table

693

steps_table = self.table(['Step', 'Status'])

694

steps = ['Build', 'Test', 'Package', 'Deploy', 'Verify']

695

696

for step in steps:

697

steps_table.add_row([step, 'Pending'])

698

steps_table.render()

699

700

# Execute with progress tracking

701

progress = self.progress_bar(len(steps))

702

progress.start()

703

704

for i, step in enumerate(steps):

705

self.line(f'<info>Executing: {step}</info>')

706

707

# Simulate step execution

708

time.sleep(1)

709

710

progress.set_message(f'Completed: {step}')

711

progress.advance()

712

713

progress.finish()

714

715

# Final status table

716

final_table = self.table(['Component', 'Status', 'URL'])

717

final_table.add_rows([

718

['Frontend', '<info>✓ Deployed</info>', 'https://app.example.com'],

719

['API', '<info>✓ Deployed</info>', 'https://api.example.com'],

720

['Database', '<info>✓ Updated</info>', 'N/A']

721

])

722

final_table.render()

723

724

self.line('<info>Deployment successful!</info>')

725

return 0

726

```