@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.
Files changed (39) hide show
  1. package/package.json +10 -3
  2. package/src/__tests__/configOnChange.test.ts +63 -0
  3. package/src/builtins/version.ts +1 -1
  4. package/src/index.ts +22 -0
  5. package/src/tui/adapters/ink/InkRenderer.tsx +4 -0
  6. package/src/tui/adapters/opentui/OpenTuiRenderer.tsx +4 -0
  7. package/src/tui/adapters/types.ts +1 -0
  8. package/src/tui/screens/ConfigScreen.tsx +6 -1
  9. package/src/tui/screens/ResultsScreen.tsx +9 -1
  10. package/src/tui/screens/RunningScreen.tsx +1 -1
  11. package/.devcontainer/devcontainer.json +0 -19
  12. package/.devcontainer/install-prerequisites.sh +0 -49
  13. package/.github/workflows/copilot-setup-steps.yml +0 -32
  14. package/.github/workflows/pull-request.yml +0 -27
  15. package/.github/workflows/release-npm-package.yml +0 -81
  16. package/AGENTS.md +0 -43
  17. package/CLAUDE.md +0 -1
  18. package/bun.lock +0 -321
  19. package/examples/tui-app/commands/config/app/get.ts +0 -62
  20. package/examples/tui-app/commands/config/app/index.ts +0 -23
  21. package/examples/tui-app/commands/config/app/set.ts +0 -96
  22. package/examples/tui-app/commands/config/index.ts +0 -28
  23. package/examples/tui-app/commands/config/user/get.ts +0 -61
  24. package/examples/tui-app/commands/config/user/index.ts +0 -23
  25. package/examples/tui-app/commands/config/user/set.ts +0 -57
  26. package/examples/tui-app/commands/greet.ts +0 -78
  27. package/examples/tui-app/commands/math.ts +0 -111
  28. package/examples/tui-app/commands/status.ts +0 -86
  29. package/examples/tui-app/index.ts +0 -38
  30. package/guides/01-hello-world.md +0 -101
  31. package/guides/02-adding-options.md +0 -103
  32. package/guides/03-multiple-commands.md +0 -161
  33. package/guides/04-subcommands.md +0 -206
  34. package/guides/05-interactive-tui.md +0 -209
  35. package/guides/06-config-validation.md +0 -256
  36. package/guides/07-async-cancellation.md +0 -334
  37. package/guides/08-complete-application.md +0 -507
  38. package/guides/README.md +0 -78
  39. package/tsconfig.json +0 -25
@@ -1,161 +0,0 @@
1
- # Guide 3: Multiple Commands (Basic)
2
-
3
- Build a CLI with multiple commands that share common functionality.
4
-
5
- ## What You'll Build
6
-
7
- A file utility CLI with `list` and `count` commands:
8
-
9
- ```bash
10
- fileutil list --dir ./src
11
- # Lists files in directory
12
-
13
- fileutil count --dir ./src --ext .ts
14
- # Counts files with extension
15
- ```
16
-
17
- ## Step 1: Create the List Command
18
-
19
- Create `src/commands/list.ts`:
20
-
21
- ```typescript
22
- import { Command, type OptionSchema, type CommandResult } from "@pablozaiden/terminatui";
23
-
24
- const options = {
25
- dir: {
26
- type: "string",
27
- description: "Directory to list",
28
- required: true,
29
- alias: "d",
30
- },
31
- } satisfies OptionSchema;
32
-
33
- export class ListCommand extends Command<typeof options> {
34
- readonly name = "list";
35
- readonly description = "List files in a directory";
36
- readonly options = options;
37
-
38
- async execute(config: { dir: string }): Promise<CommandResult> {
39
- try {
40
- const files = await Array.fromAsync(new Bun.Glob("*").scan({ cwd: config.dir, onlyFiles: false }));
41
- console.log(`Files in ${config.dir}:`);
42
- files.forEach((file) => console.log(` ${file}`));
43
- return { success: true, message: `Found ${files.length} files` };
44
- } catch (error) {
45
- return { success: false, error: String(error) };
46
- }
47
- }
48
- }
49
- ```
50
-
51
- ## Step 2: Create the Count Command
52
-
53
- Create `src/commands/count.ts`:
54
-
55
- ```typescript
56
- import { Command, type OptionSchema, type CommandResult } from "@pablozaiden/terminatui";
57
-
58
- const options = {
59
- dir: {
60
- type: "string",
61
- description: "Directory to search",
62
- required: true,
63
- alias: "d",
64
- },
65
- ext: {
66
- type: "string",
67
- description: "File extension to count (e.g., .ts)",
68
- alias: "e",
69
- },
70
- } satisfies OptionSchema;
71
-
72
- export class CountCommand extends Command<typeof options> {
73
- readonly name = "count";
74
- readonly description = "Count files in a directory";
75
- readonly options = options;
76
-
77
- async execute(config: { dir: string; ext?: string }): Promise<CommandResult> {
78
- try {
79
- let files = await Array.fromAsync(new Bun.Glob("*").scan({ cwd: config.dir, onlyFiles: false }));
80
-
81
- if (config.ext) {
82
- files = files.filter((f) => f.endsWith(config.ext!));
83
- }
84
-
85
- console.log(`Found ${files.length} files${config.ext ? ` with ${config.ext}` : ""}`);
86
- return { success: true, data: { count: files.length } };
87
- } catch (error) {
88
- return { success: false, error: String(error) };
89
- }
90
- }
91
- }
92
- ```
93
-
94
- ## Step 3: Create the Application
95
-
96
- Create `src/index.ts`:
97
-
98
- ```typescript
99
- import { Application } from "@pablozaiden/terminatui";
100
- import { ListCommand } from "./commands/list";
101
- import { CountCommand } from "./commands/count";
102
-
103
- class FileUtilApp extends Application {
104
- constructor() {
105
- super({
106
- name: "fileutil",
107
- version: "1.0.0",
108
- description: "File utility commands",
109
- commands: [
110
- new ListCommand(),
111
- new CountCommand(),
112
- ],
113
- });
114
- }
115
- }
116
-
117
- await new FileUtilApp().run();
118
- ```
119
-
120
- ## Step 4: Test It
121
-
122
- ```bash
123
- # List files
124
- bun src/index.ts list --dir ./src
125
- # Files in ./src:
126
- # commands
127
- # index.ts
128
-
129
- # Count all files
130
- bun src/index.ts count -d ./src
131
- # Found 2 files
132
-
133
- # Count only .ts files
134
- bun src/index.ts count -d ./src -e .ts
135
- # Found 1 files with .ts
136
-
137
- # Show help
138
- bun src/index.ts help
139
- # Lists: list, count, version, help
140
- ```
141
-
142
- ## Project Structure
143
-
144
- ```
145
- src/
146
- ├── index.ts # App entry point
147
- └── commands/
148
- ├── list.ts # List command
149
- └── count.ts # Count command
150
- ```
151
-
152
- ## What You Learned
153
-
154
- - Create multiple commands in separate files
155
- - Return structured `CommandResult` with data
156
- - Handle errors gracefully
157
- - Use consistent option patterns across commands
158
-
159
- ## Next Steps
160
-
161
- → [Guide 4: Subcommands](04-subcommands.md)
@@ -1,206 +0,0 @@
1
- # Guide 4: Subcommands (Basic)
2
-
3
- Organize related commands under a parent command for cleaner CLI structure.
4
-
5
- ## What You'll Build
6
-
7
- A database CLI with nested subcommands:
8
-
9
- ```bash
10
- dbctl db migrate --target latest
11
- dbctl db seed --file data.json
12
- dbctl db status
13
- ```
14
-
15
- ## Step 1: Create the Subcommands
16
-
17
- Create `src/commands/db/migrate.ts`:
18
-
19
- ```typescript
20
- import { Command, type OptionSchema, type CommandResult } from "@pablozaiden/terminatui";
21
-
22
- const options = {
23
- target: {
24
- type: "string",
25
- description: "Migration target version",
26
- default: "latest",
27
- },
28
- dry: {
29
- type: "boolean",
30
- description: "Dry run without applying",
31
- default: false,
32
- },
33
- } satisfies OptionSchema;
34
-
35
- export class MigrateCommand extends Command<typeof options> {
36
- readonly name = "migrate";
37
- readonly description = "Run database migrations";
38
- readonly options = options;
39
-
40
- execute(config: { target: string; dry: boolean }): CommandResult {
41
- ctx.logger.info(`Migrating to: ${config.target}`);
42
-
43
- if (config.dry) {
44
- console.log("DRY RUN: Would migrate to", config.target);
45
- } else {
46
- console.log("Migrating to", config.target, "...");
47
- console.log("Migration complete!");
48
- }
49
-
50
- return { success: true };
51
- }
52
- }
53
- ```
54
-
55
- Create `src/commands/db/seed.ts`:
56
-
57
- ```typescript
58
- import { Command, type OptionSchema, type CommandResult } from "@pablozaiden/terminatui";
59
-
60
- const options = {
61
- file: {
62
- type: "string",
63
- description: "Seed data file",
64
- required: true,
65
- alias: "f",
66
- },
67
- } satisfies OptionSchema;
68
-
69
- export class SeedCommand extends Command<typeof options> {
70
- readonly name = "seed";
71
- readonly description = "Seed the database with data";
72
- readonly options = options;
73
-
74
- execute(config: { file: string }): CommandResult {
75
- ctx.logger.info(`Seeding from: ${config.file}`);
76
- console.log(`Loading seed data from ${config.file}...`);
77
- console.log("Database seeded successfully!");
78
- return { success: true };
79
- }
80
- }
81
- ```
82
-
83
- Create `src/commands/db/status.ts`:
84
-
85
- ```typescript
86
- import { Command, type CommandResult } from "@pablozaiden/terminatui";
87
-
88
- export class StatusCommand extends Command {
89
- readonly name = "status";
90
- readonly description = "Show database status";
91
- readonly options = {};
92
-
93
- execute(): CommandResult {
94
- console.log("Database Status:");
95
- console.log(" Connected: Yes");
96
- console.log(" Version: 1.2.3");
97
- console.log(" Migrations: 5 applied");
98
- return { success: true };
99
- }
100
- }
101
- ```
102
-
103
- ## Step 2: Create the Parent Command
104
-
105
- Create `src/commands/db/index.ts`:
106
-
107
- ```typescript
108
- import { Command, type CommandResult } from "@pablozaiden/terminatui";
109
- import { MigrateCommand } from "./migrate";
110
- import { SeedCommand } from "./seed";
111
- import { StatusCommand } from "./status";
112
-
113
- export class DbCommand extends Command {
114
- readonly name = "db";
115
- readonly description = "Database operations";
116
- readonly options = {};
117
-
118
- // Subcommands are nested here
119
- override readonly subCommands = [
120
- new MigrateCommand(),
121
- new SeedCommand(),
122
- new StatusCommand(),
123
- ];
124
-
125
- // Parent command can have its own execute (optional)
126
- execute(): CommandResult {
127
- console.log("Use 'dbctl db <command>' for database operations.");
128
- console.log("Available: migrate, seed, status");
129
- return { success: true };
130
- }
131
- }
132
- ```
133
-
134
- ## Step 3: Create the Application
135
-
136
- Create `src/index.ts`:
137
-
138
- ```typescript
139
- import { Application } from "@pablozaiden/terminatui";
140
- import { DbCommand } from "./commands/db";
141
-
142
- class DbCtlApp extends Application {
143
- constructor() {
144
- super({
145
- name: "dbctl",
146
- version: "1.0.0",
147
- description: "Database control CLI",
148
- commands: [new DbCommand()],
149
- });
150
- }
151
- }
152
-
153
- await new DbCtlApp().run();
154
- ```
155
-
156
- ## Step 4: Test It
157
-
158
- ```bash
159
- # Show db command help
160
- bun src/index.ts db help
161
- # Shows: migrate, seed, status
162
-
163
- # Run migration
164
- bun src/index.ts db migrate
165
- # Migrating to latest...
166
-
167
- # Dry run migration
168
- bun src/index.ts db migrate --target v2 --dry
169
- # DRY RUN: Would migrate to v2
170
-
171
- # Seed database
172
- bun src/index.ts db seed -f data.json
173
- # Loading seed data from data.json...
174
-
175
- # Check status
176
- bun src/index.ts db status
177
- # Database Status: ...
178
-
179
- # Get help for subcommand
180
- bun src/index.ts db migrate help
181
- # Shows migrate options
182
- ```
183
-
184
- ## Project Structure
185
-
186
- ```
187
- src/
188
- ├── index.ts
189
- └── commands/
190
- └── db/
191
- ├── index.ts # Parent command
192
- ├── migrate.ts # Subcommand
193
- ├── seed.ts # Subcommand
194
- └── status.ts # Subcommand
195
- ```
196
-
197
- ## What You Learned
198
-
199
- - Group related commands under a parent
200
- - Define subcommands with `subCommands` property
201
- - Each subcommand has its own options
202
- - Help is automatically nested (`db help`, `db migrate help`)
203
-
204
- ## Next Steps
205
-
206
- → [Guide 5: Interactive TUI](05-interactive-tui.md)
@@ -1,209 +0,0 @@
1
- # Guide 5: Interactive TUI
2
-
3
- Add an auto-generated Terminal User Interface to your CLI.
4
-
5
- ## What You'll Build
6
-
7
- A task runner with both CLI and interactive TUI modes:
8
-
9
- ```bash
10
- # CLI mode
11
- taskr --mode cli run --task build --env production
12
-
13
- # TUI mode
14
- # (if your app's default mode is a TUI mode)
15
- taskr
16
-
17
- # Force TUI mode
18
- taskr --mode opentui
19
- # or
20
- taskr --mode ink
21
-
22
- # Force CLI mode
23
- taskr --mode cli
24
- ```
25
-
26
- Only the selected mode (`--mode`) or your app's default mode controls whether the app runs in CLI or TUI.
27
-
28
- ## Step 1: Create the Command with TUI Metadata
29
-
30
- Create `src/commands/run.ts`:
31
-
32
- ```typescript
33
- import {
34
- Command,
35
- type OptionSchema,
36
- type CommandResult,
37
- type CommandExecutionContext,
38
- } from "@pablozaiden/terminatui";
39
-
40
- const options = {
41
- task: {
42
- type: "string",
43
- description: "Task to run",
44
- required: true,
45
- enum: ["build", "test", "lint", "deploy"],
46
- // TUI metadata
47
- label: "Task",
48
- order: 1,
49
- group: "Required",
50
- },
51
- env: {
52
- type: "string",
53
- description: "Environment",
54
- default: "development",
55
- enum: ["development", "staging", "production"],
56
- // TUI metadata
57
- label: "Environment",
58
- order: 2,
59
- group: "Configuration",
60
- },
61
- verbose: {
62
- type: "boolean",
63
- description: "Verbose output",
64
- default: false,
65
- // TUI metadata
66
- label: "Verbose Mode",
67
- order: 10,
68
- group: "Options",
69
- },
70
- } satisfies OptionSchema;
71
-
72
- interface RunConfig {
73
- task: string;
74
- env: string;
75
- verbose: boolean;
76
- }
77
-
78
- export class RunCommand extends Command<typeof options, RunConfig> {
79
- readonly name = "run";
80
- readonly description = "Run a task";
81
- readonly options = options;
82
-
83
- // TUI customization
84
- override readonly displayName = "Run Task";
85
- override readonly actionLabel = "Start Task";
86
-
87
- async execute(config: RunConfig, _execCtx: CommandExecutionContext): Promise<CommandResult> {
88
- if (config.verbose) {
89
- console.debug(`Environment: ${config.env}`);
90
- }
91
-
92
- // Simulate task execution
93
- console.log(`Running ${config.task} in ${config.env}...`);
94
- await new Promise((resolve) => setTimeout(resolve, 1000));
95
- console.log("Task completed!");
96
-
97
- return {
98
- success: true,
99
- data: { task: config.task, env: config.env },
100
- message: `Task ${config.task} completed successfully`,
101
- };
102
- }
103
- }
104
- ```
105
-
106
- ## Step 2: Create the TUI Application
107
-
108
- Create `src/index.ts`:
109
-
110
- ```typescript
111
- import { TuiApplication } from "@pablozaiden/terminatui";
112
- import { RunCommand } from "./commands/run";
113
-
114
- class TaskRunnerApp extends TuiApplication {
115
- // Default is CLI; each app decides.
116
- protected override defaultMode = "opentui" as const;
117
-
118
- constructor() {
119
- super({
120
- name: "taskr",
121
- displayName: "🚀 Task Runner", // Shown in TUI header
122
- version: "1.0.0",
123
- commands: [new RunCommand()],
124
- enableTui: true, // Default: true
125
- });
126
- }
127
- }
128
-
129
- await new TaskRunnerApp().run();
130
- ```
131
-
132
- ## Step 3: Test Both Modes
133
-
134
- **CLI Mode** (forced):
135
-
136
- ```bash
137
- bun src/index.ts --mode cli run --task build --env production
138
- # Running build in production...
139
- # Task completed!
140
- ```
141
-
142
- **TUI Mode** (forced):
143
-
144
- ```bash
145
- bun src/index.ts --mode opentui
146
- ```
147
-
148
- This opens an interactive interface:
149
- - Use ↑/↓ to navigate fields
150
- - Press Enter to edit a field
151
- - Navigate to "CLI Command" button and press Enter to see the CLI command
152
- - Press Enter on "Start Task" to run
153
- - Press Esc to go back
154
-
155
- ## TUI Metadata Reference
156
-
157
- Add these properties to your options for TUI customization:
158
-
159
- ```typescript
160
- {
161
- type: "string",
162
- description: "...",
163
-
164
- // TUI-specific
165
- label: "Display Label", // Custom field label
166
- order: 1, // Field sort order
167
- group: "Settings", // Group heading
168
- placeholder: "Enter...", // Placeholder text
169
- tuiHidden: false, // Hide from TUI (still in CLI)
170
- }
171
- ```
172
-
173
- ## Command TUI Properties
174
-
175
- ```typescript
176
- class MyCommand extends Command {
177
- // Display name in command selector
178
- override readonly displayName = "My Command";
179
-
180
- // Button text (default: "Run")
181
- override readonly actionLabel = "Execute";
182
-
183
- // Skip config screen, run immediately
184
- override readonly immediateExecution = false;
185
- }
186
- ```
187
-
188
- ## Keyboard Shortcuts
189
-
190
- | Key | Action |
191
- |-----|--------|
192
- | ↑/↓ | Navigate fields |
193
- | Enter | Edit field / Press button / Run |
194
- | Tab | Cycle focus |
195
- | L | Toggle logs |
196
- | Ctrl+Y | Copy to clipboard |
197
- | Esc | Back / Cancel |
198
-
199
- ## What You Learned
200
-
201
- - Use `TuiApplication` instead of `Application`
202
- - Use `--mode` (or app default mode) to control CLI vs TUI
203
- - Add TUI metadata to options (label, order, group)
204
- - Customize with `displayName` and `actionLabel`
205
- - Both CLI and TUI work with the same command
206
-
207
- ## Next Steps
208
-
209
- → [Guide 6: Config Validation](06-config-validation.md)