@pablozaiden/terminatui 0.3.0-beta-1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -3
- package/src/__tests__/configOnChange.test.ts +63 -0
- package/src/builtins/version.ts +1 -1
- package/src/index.ts +22 -0
- package/src/tui/adapters/ink/InkRenderer.tsx +4 -0
- package/src/tui/adapters/opentui/OpenTuiRenderer.tsx +4 -0
- package/src/tui/adapters/types.ts +1 -0
- package/src/tui/screens/ConfigScreen.tsx +6 -1
- package/src/tui/screens/ResultsScreen.tsx +9 -1
- package/src/tui/screens/RunningScreen.tsx +1 -1
- package/.devcontainer/devcontainer.json +0 -19
- package/.devcontainer/install-prerequisites.sh +0 -49
- package/.github/workflows/copilot-setup-steps.yml +0 -32
- package/.github/workflows/pull-request.yml +0 -27
- package/.github/workflows/release-npm-package.yml +0 -81
- package/AGENTS.md +0 -43
- package/CLAUDE.md +0 -1
- package/bun.lock +0 -321
- package/examples/tui-app/commands/config/app/get.ts +0 -62
- package/examples/tui-app/commands/config/app/index.ts +0 -23
- package/examples/tui-app/commands/config/app/set.ts +0 -96
- package/examples/tui-app/commands/config/index.ts +0 -28
- package/examples/tui-app/commands/config/user/get.ts +0 -61
- package/examples/tui-app/commands/config/user/index.ts +0 -23
- package/examples/tui-app/commands/config/user/set.ts +0 -57
- package/examples/tui-app/commands/greet.ts +0 -78
- package/examples/tui-app/commands/math.ts +0 -111
- package/examples/tui-app/commands/status.ts +0 -86
- package/examples/tui-app/index.ts +0 -38
- package/guides/01-hello-world.md +0 -101
- package/guides/02-adding-options.md +0 -103
- package/guides/03-multiple-commands.md +0 -161
- package/guides/04-subcommands.md +0 -206
- package/guides/05-interactive-tui.md +0 -209
- package/guides/06-config-validation.md +0 -256
- package/guides/07-async-cancellation.md +0 -334
- package/guides/08-complete-application.md +0 -507
- package/guides/README.md +0 -78
- package/tsconfig.json +0 -25
|
@@ -1,507 +0,0 @@
|
|
|
1
|
-
# Guide 8: Building a Complete Application (Complex)
|
|
2
|
-
|
|
3
|
-
Bring together everything to build a production-ready CLI application with multiple commands, services, logging, and a polished TUI.
|
|
4
|
-
|
|
5
|
-
## What You'll Build
|
|
6
|
-
|
|
7
|
-
A task management CLI with:
|
|
8
|
-
- Multiple commands: list, add, complete, stats
|
|
9
|
-
- Shared services (database, notifications)
|
|
10
|
-
- Logging with configurable verbosity
|
|
11
|
-
- Full TUI support with custom result rendering
|
|
12
|
-
- Config validation and type safety
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
tasks add "Write documentation" --priority high
|
|
16
|
-
tasks list --filter pending
|
|
17
|
-
tasks complete abc123
|
|
18
|
-
tasks stats
|
|
19
|
-
tasks --tui # Interactive mode
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Project Structure
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
src/
|
|
26
|
-
├── index.ts # Application entry
|
|
27
|
-
├── services/
|
|
28
|
-
│ ├── database.ts # Task storage
|
|
29
|
-
│ └── notifications.ts # Task notifications
|
|
30
|
-
├── commands/
|
|
31
|
-
│ ├── add.ts
|
|
32
|
-
│ ├── list.ts
|
|
33
|
-
│ ├── complete.ts
|
|
34
|
-
│ └── stats.ts
|
|
35
|
-
└── types.ts # Shared types
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Step 1: Define Shared Types
|
|
39
|
-
|
|
40
|
-
Create `src/types.ts`:
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
export interface Task {
|
|
44
|
-
id: string;
|
|
45
|
-
title: string;
|
|
46
|
-
priority: "low" | "medium" | "high";
|
|
47
|
-
status: "pending" | "completed";
|
|
48
|
-
createdAt: Date;
|
|
49
|
-
completedAt?: Date;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface TaskStats {
|
|
53
|
-
total: number;
|
|
54
|
-
pending: number;
|
|
55
|
-
completed: number;
|
|
56
|
-
byPriority: Record<string, number>;
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## Step 2: Create Services
|
|
61
|
-
|
|
62
|
-
Create `src/services/database.ts`:
|
|
63
|
-
|
|
64
|
-
```typescript
|
|
65
|
-
import type { Task, TaskStats } from "../types";
|
|
66
|
-
|
|
67
|
-
// In-memory database (replace with real DB in production)
|
|
68
|
-
const tasks: Map<string, Task> = new Map();
|
|
69
|
-
|
|
70
|
-
export class Database {
|
|
71
|
-
async addTask(title: string, priority: Task["priority"]): Promise<Task> {
|
|
72
|
-
const id = Math.random().toString(36).substring(7);
|
|
73
|
-
const task: Task = {
|
|
74
|
-
id,
|
|
75
|
-
title,
|
|
76
|
-
priority,
|
|
77
|
-
status: "pending",
|
|
78
|
-
createdAt: new Date(),
|
|
79
|
-
};
|
|
80
|
-
tasks.set(id, task);
|
|
81
|
-
return task;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async getTask(id: string): Promise<Task | undefined> {
|
|
85
|
-
return tasks.get(id);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async listTasks(filter?: "pending" | "completed" | "all"): Promise<Task[]> {
|
|
89
|
-
const all = Array.from(tasks.values());
|
|
90
|
-
if (!filter || filter === "all") return all;
|
|
91
|
-
return all.filter((t) => t.status === filter);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async completeTask(id: string): Promise<Task | undefined> {
|
|
95
|
-
const task = tasks.get(id);
|
|
96
|
-
if (task) {
|
|
97
|
-
task.status = "completed";
|
|
98
|
-
task.completedAt = new Date();
|
|
99
|
-
tasks.set(id, task);
|
|
100
|
-
}
|
|
101
|
-
return task;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async getStats(): Promise<TaskStats> {
|
|
105
|
-
const all = Array.from(tasks.values());
|
|
106
|
-
return {
|
|
107
|
-
total: all.length,
|
|
108
|
-
pending: all.filter((t) => t.status === "pending").length,
|
|
109
|
-
completed: all.filter((t) => t.status === "completed").length,
|
|
110
|
-
byPriority: {
|
|
111
|
-
high: all.filter((t) => t.priority === "high").length,
|
|
112
|
-
medium: all.filter((t) => t.priority === "medium").length,
|
|
113
|
-
low: all.filter((t) => t.priority === "low").length,
|
|
114
|
-
},
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Create `src/services/notifications.ts`:
|
|
121
|
-
|
|
122
|
-
```typescript
|
|
123
|
-
import type { Task } from "../types";
|
|
124
|
-
import type { Logger } from "@pablozaiden/terminatui";
|
|
125
|
-
|
|
126
|
-
export class NotificationService {
|
|
127
|
-
constructor(private logger: Logger) {}
|
|
128
|
-
|
|
129
|
-
taskAdded(task: Task): void {
|
|
130
|
-
this.logger.info(`📝 Task added: ${task.title} [${task.priority}]`);
|
|
131
|
-
if (task.priority === "high") {
|
|
132
|
-
this.logger.warn(`⚠️ High priority task created!`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
taskCompleted(task: Task): void {
|
|
137
|
-
this.logger.info(`✅ Task completed: ${task.title}`);
|
|
138
|
-
const duration = task.completedAt!.getTime() - task.createdAt.getTime();
|
|
139
|
-
const hours = Math.floor(duration / 3600000);
|
|
140
|
-
if (hours > 24) {
|
|
141
|
-
this.logger.debug(`Task took ${hours} hours to complete`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## Step 3: Create Commands
|
|
148
|
-
|
|
149
|
-
Create `src/commands/add.ts`:
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
import {
|
|
153
|
-
Command,
|
|
154
|
-
ConfigValidationError,
|
|
155
|
-
type OptionSchema,
|
|
156
|
-
type OptionValues,
|
|
157
|
-
type CommandResult,
|
|
158
|
-
} from "@pablozaiden/terminatui";
|
|
159
|
-
import { Database } from "../services/database";
|
|
160
|
-
import { NotificationService } from "../services/notifications";
|
|
161
|
-
import type { Task } from "../types";
|
|
162
|
-
|
|
163
|
-
const options = {
|
|
164
|
-
title: {
|
|
165
|
-
type: "string",
|
|
166
|
-
description: "Task title",
|
|
167
|
-
required: true,
|
|
168
|
-
label: "Task Title",
|
|
169
|
-
order: 1,
|
|
170
|
-
},
|
|
171
|
-
priority: {
|
|
172
|
-
type: "string",
|
|
173
|
-
description: "Task priority",
|
|
174
|
-
default: "medium",
|
|
175
|
-
enum: ["low", "medium", "high"],
|
|
176
|
-
label: "Priority",
|
|
177
|
-
order: 2,
|
|
178
|
-
},
|
|
179
|
-
} satisfies OptionSchema;
|
|
180
|
-
|
|
181
|
-
interface AddConfig {
|
|
182
|
-
title: string;
|
|
183
|
-
priority: Task["priority"];
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export class AddCommand extends Command<typeof options, AddConfig> {
|
|
187
|
-
readonly name = "add";
|
|
188
|
-
readonly description = "Add a new task";
|
|
189
|
-
readonly options = options;
|
|
190
|
-
readonly displayName = "Add Task";
|
|
191
|
-
readonly actionLabel = "Create Task";
|
|
192
|
-
|
|
193
|
-
private db = new Database();
|
|
194
|
-
|
|
195
|
-
override buildConfig(opts: OptionValues<typeof options>): AddConfig {
|
|
196
|
-
const title = (opts["title"] as string)?.trim();
|
|
197
|
-
if (!title) {
|
|
198
|
-
throw new ConfigValidationError("Task title cannot be empty", "title");
|
|
199
|
-
}
|
|
200
|
-
if (title.length > 200) {
|
|
201
|
-
throw new ConfigValidationError(
|
|
202
|
-
"Task title must be 200 characters or less",
|
|
203
|
-
"title"
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const priority = opts["priority"] as Task["priority"] ?? "medium";
|
|
208
|
-
|
|
209
|
-
return { title, priority };
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
async execute(config: AddConfig): Promise<CommandResult> {
|
|
213
|
-
const task = await this.db.addTask(config.title, config.priority);
|
|
214
|
-
|
|
215
|
-
// (Example) optional: log to console or your own logger
|
|
216
|
-
console.log(`Created task: ${task.title}`);
|
|
217
|
-
|
|
218
|
-
return {
|
|
219
|
-
success: true,
|
|
220
|
-
data: task,
|
|
221
|
-
message: `Created task: ${task.title} (${task.id})`,
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
Create `src/commands/list.ts`:
|
|
228
|
-
|
|
229
|
-
```typescript
|
|
230
|
-
import {
|
|
231
|
-
Command,
|
|
232
|
-
type OptionSchema,
|
|
233
|
-
type OptionValues,
|
|
234
|
-
type CommandResult,
|
|
235
|
-
} from "@pablozaiden/terminatui";
|
|
236
|
-
import { Database } from "../services/database";
|
|
237
|
-
import type { Task } from "../types";
|
|
238
|
-
|
|
239
|
-
const options = {
|
|
240
|
-
filter: {
|
|
241
|
-
type: "string",
|
|
242
|
-
description: "Filter by status",
|
|
243
|
-
default: "all",
|
|
244
|
-
enum: ["all", "pending", "completed"],
|
|
245
|
-
label: "Status Filter",
|
|
246
|
-
},
|
|
247
|
-
priority: {
|
|
248
|
-
type: "string",
|
|
249
|
-
description: "Filter by priority",
|
|
250
|
-
enum: ["low", "medium", "high"],
|
|
251
|
-
label: "Priority Filter",
|
|
252
|
-
},
|
|
253
|
-
} satisfies OptionSchema;
|
|
254
|
-
|
|
255
|
-
interface ListConfig {
|
|
256
|
-
filter: "all" | "pending" | "completed";
|
|
257
|
-
priority?: Task["priority"];
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
export class ListCommand extends Command<typeof options, ListConfig> {
|
|
261
|
-
readonly name = "list";
|
|
262
|
-
readonly description = "List tasks";
|
|
263
|
-
readonly options = options;
|
|
264
|
-
readonly displayName = "List Tasks";
|
|
265
|
-
readonly actionLabel = "Show Tasks";
|
|
266
|
-
|
|
267
|
-
// Execute immediately when selected in TUI
|
|
268
|
-
readonly immediateExecution = true;
|
|
269
|
-
|
|
270
|
-
private db = new Database();
|
|
271
|
-
|
|
272
|
-
override buildConfig(opts: OptionValues<typeof options>): ListConfig {
|
|
273
|
-
return {
|
|
274
|
-
filter: (opts["filter"] as ListConfig["filter"]) ?? "all",
|
|
275
|
-
priority: opts["priority"] as Task["priority"] | undefined,
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
async execute(config: ListConfig): Promise<CommandResult> {
|
|
280
|
-
let tasks = await this.db.listTasks(config.filter);
|
|
281
|
-
|
|
282
|
-
if (config.priority) {
|
|
283
|
-
tasks = tasks.filter((t) => t.priority === config.priority);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
return {
|
|
287
|
-
success: true,
|
|
288
|
-
data: tasks,
|
|
289
|
-
message: `Found ${tasks.length} tasks`,
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
Create `src/commands/complete.ts`:
|
|
296
|
-
|
|
297
|
-
```typescript
|
|
298
|
-
import {
|
|
299
|
-
Command,
|
|
300
|
-
ConfigValidationError,
|
|
301
|
-
type OptionSchema,
|
|
302
|
-
type OptionValues,
|
|
303
|
-
type CommandResult,
|
|
304
|
-
} from "@pablozaiden/terminatui";
|
|
305
|
-
import { Database } from "../services/database";
|
|
306
|
-
import { NotificationService } from "../services/notifications";
|
|
307
|
-
import type { Task } from "../types";
|
|
308
|
-
|
|
309
|
-
const options = {
|
|
310
|
-
id: {
|
|
311
|
-
type: "string",
|
|
312
|
-
description: "Task ID to complete",
|
|
313
|
-
required: true,
|
|
314
|
-
label: "Task ID",
|
|
315
|
-
},
|
|
316
|
-
} satisfies OptionSchema;
|
|
317
|
-
|
|
318
|
-
interface CompleteConfig {
|
|
319
|
-
id: string;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
export class CompleteCommand extends Command<typeof options, CompleteConfig> {
|
|
323
|
-
readonly name = "complete";
|
|
324
|
-
readonly description = "Mark a task as complete";
|
|
325
|
-
readonly options = options;
|
|
326
|
-
readonly displayName = "Complete Task";
|
|
327
|
-
readonly actionLabel = "Mark Complete";
|
|
328
|
-
|
|
329
|
-
private db = new Database();
|
|
330
|
-
|
|
331
|
-
override buildConfig(opts: OptionValues<typeof options>): CompleteConfig {
|
|
332
|
-
const id = opts["id"] as string;
|
|
333
|
-
if (!id?.trim()) {
|
|
334
|
-
throw new ConfigValidationError("Task ID is required", "id");
|
|
335
|
-
}
|
|
336
|
-
return { id: id.trim() };
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
async execute(config: CompleteConfig): Promise<CommandResult> {
|
|
340
|
-
const task = await this.db.completeTask(config.id);
|
|
341
|
-
|
|
342
|
-
if (!task) {
|
|
343
|
-
return {
|
|
344
|
-
success: false,
|
|
345
|
-
message: `Task not found: ${config.id}`,
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
console.log(`Completed task: ${task.title}`);
|
|
350
|
-
|
|
351
|
-
return {
|
|
352
|
-
success: true,
|
|
353
|
-
data: task,
|
|
354
|
-
message: `Completed: ${task.title}`,
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
Create `src/commands/stats.ts`:
|
|
361
|
-
|
|
362
|
-
```typescript
|
|
363
|
-
import {
|
|
364
|
-
Command,
|
|
365
|
-
type OptionSchema,
|
|
366
|
-
type CommandResult,
|
|
367
|
-
} from "@pablozaiden/terminatui";
|
|
368
|
-
import { Database } from "../services/database";
|
|
369
|
-
import type { TaskStats } from "../types";
|
|
370
|
-
|
|
371
|
-
const options = {} satisfies OptionSchema;
|
|
372
|
-
|
|
373
|
-
export class StatsCommand extends Command<typeof options> {
|
|
374
|
-
readonly name = "stats";
|
|
375
|
-
readonly description = "Show task statistics";
|
|
376
|
-
readonly options = options;
|
|
377
|
-
readonly displayName = "Statistics";
|
|
378
|
-
readonly actionLabel = "Show Stats";
|
|
379
|
-
readonly immediateExecution = true;
|
|
380
|
-
|
|
381
|
-
private db = new Database();
|
|
382
|
-
|
|
383
|
-
async execute(): Promise<CommandResult> {
|
|
384
|
-
const stats = await this.db.getStats();
|
|
385
|
-
|
|
386
|
-
return {
|
|
387
|
-
success: true,
|
|
388
|
-
data: stats,
|
|
389
|
-
message: `Total: ${stats.total}, Pending: ${stats.pending}, Completed: ${stats.completed}`,
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
## Step 4: Create the Application
|
|
396
|
-
|
|
397
|
-
Create `src/index.ts`:
|
|
398
|
-
|
|
399
|
-
```typescript
|
|
400
|
-
import { TuiApplication } from "@pablozaiden/terminatui";
|
|
401
|
-
import { AddCommand } from "./commands/add";
|
|
402
|
-
import { ListCommand } from "./commands/list";
|
|
403
|
-
import { CompleteCommand } from "./commands/complete";
|
|
404
|
-
import { StatsCommand } from "./commands/stats";
|
|
405
|
-
|
|
406
|
-
class TasksCLI extends TuiApplication {
|
|
407
|
-
constructor() {
|
|
408
|
-
super({
|
|
409
|
-
name: "tasks",
|
|
410
|
-
version: "1.0.0",
|
|
411
|
-
description: "A simple task management CLI",
|
|
412
|
-
commands: [
|
|
413
|
-
new AddCommand(),
|
|
414
|
-
new ListCommand(),
|
|
415
|
-
new CompleteCommand(),
|
|
416
|
-
new StatsCommand(),
|
|
417
|
-
],
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Optional: Override onBeforeRun for setup
|
|
422
|
-
protected override async onBeforeRun(): Promise<void> {
|
|
423
|
-
this.logger.debug("Tasks CLI starting...");
|
|
424
|
-
// Initialize database connections, load config, etc.
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Optional: Override onAfterRun for cleanup
|
|
428
|
-
protected override async onAfterRun(): Promise<void> {
|
|
429
|
-
this.logger.debug("Tasks CLI shutting down...");
|
|
430
|
-
// Close database connections, save state, etc.
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
await new TasksCLI().run();
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
## Step 5: Configure Package
|
|
438
|
-
|
|
439
|
-
Update `package.json`:
|
|
440
|
-
|
|
441
|
-
```json
|
|
442
|
-
{
|
|
443
|
-
"name": "tasks-cli",
|
|
444
|
-
"version": "1.0.0",
|
|
445
|
-
"type": "module",
|
|
446
|
-
"bin": {
|
|
447
|
-
"tasks": "./src/index.ts"
|
|
448
|
-
},
|
|
449
|
-
"scripts": {
|
|
450
|
-
"start": "bun src/index.ts",
|
|
451
|
-
"tui": "bun src/index.ts --tui",
|
|
452
|
-
"build": "bun build src/index.ts --outdir dist --target bun"
|
|
453
|
-
},
|
|
454
|
-
"dependencies": {
|
|
455
|
-
"@pablozaiden/terminatui": "^1.0.0"
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
## Step 6: Run the Application
|
|
461
|
-
|
|
462
|
-
```bash
|
|
463
|
-
# CLI Mode
|
|
464
|
-
bun start add "Write documentation" --priority high
|
|
465
|
-
bun start list --filter pending
|
|
466
|
-
bun start complete abc123
|
|
467
|
-
bun start stats
|
|
468
|
-
|
|
469
|
-
# TUI Mode
|
|
470
|
-
bun tui
|
|
471
|
-
|
|
472
|
-
# With verbose logging
|
|
473
|
-
bun start --verbose add "Debug task" --priority low
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
## TUI Feature Summary
|
|
477
|
-
|
|
478
|
-
| Feature | Implementation |
|
|
479
|
-
|---------|----------------|
|
|
480
|
-
| Command Groups | `group = "Tasks"` or `"Analytics"` |
|
|
481
|
-
| Command Order | `order = 1, 2, 3` |
|
|
482
|
-
| Display Names | `displayName = "Add Task"` |
|
|
483
|
-
| Action Labels | `actionLabel = "Create Task"` |
|
|
484
|
-
| Immediate Execution | `immediateExecution = true` |
|
|
485
|
-
| Clipboard Support | `getClipboardContent()` |
|
|
486
|
-
|
|
487
|
-
## What You Learned
|
|
488
|
-
|
|
489
|
-
- **Project Structure**: Organize code into commands, services, types
|
|
490
|
-
- **Shared Services**: Database and notification services
|
|
491
|
-
- **Command Groups**: Organize commands in TUI sidebar
|
|
492
|
-
- **Full TUI Integration**: Clipboard, immediate execution
|
|
493
|
-
- **Lifecycle Hooks**: `onBeforeRun` and `onAfterRun`
|
|
494
|
-
- **Production Patterns**: Error handling, validation, logging
|
|
495
|
-
|
|
496
|
-
## Complete Series Summary
|
|
497
|
-
|
|
498
|
-
1. **Hello World** - Basic command structure
|
|
499
|
-
2. **Adding Options** - Option types and defaults
|
|
500
|
-
3. **Multiple Commands** - Command organization
|
|
501
|
-
4. **Subcommands** - Nested command hierarchies
|
|
502
|
-
5. **Interactive TUI** - TuiApplication basics
|
|
503
|
-
6. **Config Validation** - buildConfig and validation
|
|
504
|
-
7. **Async Cancellation** - Cancellable operations
|
|
505
|
-
8. **Complete Application** - Full production app
|
|
506
|
-
|
|
507
|
-
You now have all the tools to build powerful CLI applications with terminatui! 🎉
|
package/guides/README.md
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
# Terminatui Framework Guides
|
|
2
|
-
|
|
3
|
-
Step-by-step tutorials for building CLI applications with the terminatui framework.
|
|
4
|
-
|
|
5
|
-
## Guide Overview
|
|
6
|
-
|
|
7
|
-
| # | Guide | Level | Topics |
|
|
8
|
-
|---|-------|-------|--------|
|
|
9
|
-
| 1 | [Hello World](01-hello-world.md) | Super Simple | Basic Command, Application |
|
|
10
|
-
| 2 | [Adding Options](02-adding-options.md) | Super Simple | Option types, defaults, aliases |
|
|
11
|
-
| 3 | [Multiple Commands](03-multiple-commands.md) | Basic | Multiple commands, project structure |
|
|
12
|
-
| 4 | [Subcommands](04-subcommands.md) | Basic | Nested subcommands, hierarchies |
|
|
13
|
-
| 5 | [Interactive TUI](05-interactive-tui.md) | Normal | TuiApplication, metadata, keyboard |
|
|
14
|
-
| 6 | [Config Validation](06-config-validation.md) | Normal | buildConfig, ConfigValidationError |
|
|
15
|
-
| 7 | [Async Cancellation](07-async-cancellation.md) | Complex | AbortSignal, cancellation, cleanup |
|
|
16
|
-
| 8 | [Complete Application](08-complete-application.md) | Complex | Full app, services, best practices |
|
|
17
|
-
|
|
18
|
-
## Learning Path
|
|
19
|
-
|
|
20
|
-
### Beginners
|
|
21
|
-
Start with guides 1-2 to understand the basics of commands and options.
|
|
22
|
-
|
|
23
|
-
### Intermediate
|
|
24
|
-
Continue with guides 3-4 to learn about organizing larger applications.
|
|
25
|
-
|
|
26
|
-
### Advanced
|
|
27
|
-
Work through guides 5-8 to master TUI features, validation, and production patterns.
|
|
28
|
-
|
|
29
|
-
## Quick Start
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
# Create a new project
|
|
33
|
-
mkdir my-cli && cd my-cli
|
|
34
|
-
bun init -y
|
|
35
|
-
bun add @pablozaiden/terminatui
|
|
36
|
-
|
|
37
|
-
# Create your first command
|
|
38
|
-
cat > src/index.ts << 'EOF'
|
|
39
|
-
import { Command, Application, type OptionSchema } from "@pablozaiden/terminatui";
|
|
40
|
-
|
|
41
|
-
const options = {
|
|
42
|
-
name: { type: "string", description: "Your name" },
|
|
43
|
-
} satisfies OptionSchema;
|
|
44
|
-
|
|
45
|
-
class HelloCommand extends Command<typeof options> {
|
|
46
|
-
readonly name = "hello";
|
|
47
|
-
readonly description = "Say hello";
|
|
48
|
-
readonly options = options;
|
|
49
|
-
|
|
50
|
-
async execute(config: Record<string, unknown>) {
|
|
51
|
-
const name = config["name"] ?? "World";
|
|
52
|
-
console.log(`Hello, ${name}!`);
|
|
53
|
-
return { success: true };
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
class MyCLI extends Application {
|
|
58
|
-
constructor() {
|
|
59
|
-
super({ name: "my-cli", version: "1.0.0", commands: [new HelloCommand()] });
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Recommended: let Terminatui read `Bun.argv.slice(2)`
|
|
64
|
-
await new MyCLI().run();
|
|
65
|
-
|
|
66
|
-
// For tests or programmatic invocation:
|
|
67
|
-
// await new MyCLI().runFromArgs(["hello", "--name", "Developer"]);
|
|
68
|
-
EOF
|
|
69
|
-
|
|
70
|
-
# Run it
|
|
71
|
-
bun src/index.ts hello --name "Developer"
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## Prerequisites
|
|
75
|
-
|
|
76
|
-
- [Bun](https://bun.sh)
|
|
77
|
-
- Basic TypeScript knowledge
|
|
78
|
-
- Terminal/command-line familiarity
|
package/tsconfig.json
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"lib": ["ESNext"],
|
|
4
|
-
"target": "ESNext",
|
|
5
|
-
"module": "ESNext",
|
|
6
|
-
"moduleDetection": "force",
|
|
7
|
-
"jsx": "react-jsx",
|
|
8
|
-
"jsxImportSource": "@opentui/react",
|
|
9
|
-
"allowJs": true,
|
|
10
|
-
"moduleResolution": "bundler",
|
|
11
|
-
"allowImportingTsExtensions": true,
|
|
12
|
-
"verbatimModuleSyntax": true,
|
|
13
|
-
"noEmit": true,
|
|
14
|
-
"strict": true,
|
|
15
|
-
"skipLibCheck": true,
|
|
16
|
-
"noFallthroughCasesInSwitch": true,
|
|
17
|
-
"noUnusedLocals": true,
|
|
18
|
-
"noUnusedParameters": true,
|
|
19
|
-
"noPropertyAccessFromIndexSignature": true,
|
|
20
|
-
"noUncheckedIndexedAccess": true,
|
|
21
|
-
"esModuleInterop": true,
|
|
22
|
-
"types": ["bun"]
|
|
23
|
-
},
|
|
24
|
-
"include": ["src/**/*", "examples/**/*"]
|
|
25
|
-
}
|