@rlabs-inc/create-tui 0.1.6 → 0.2.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/README.md +129 -0
  2. package/dist/index.js +221 -0
  3. package/package.json +20 -15
  4. package/templates/counter/README.md +88 -0
  5. package/templates/counter/package.json +18 -0
  6. package/templates/counter/src/App.ts +65 -0
  7. package/templates/counter/src/components/Counter.ts +78 -0
  8. package/templates/counter/src/components/CounterPanel.ts +74 -0
  9. package/templates/counter/src/components/Header.ts +55 -0
  10. package/templates/counter/src/components/HistoryPanel.ts +96 -0
  11. package/templates/counter/src/components/KeyBindings.ts +60 -0
  12. package/templates/counter/src/components/StatsPanel.ts +101 -0
  13. package/templates/counter/src/main.ts +87 -0
  14. package/templates/counter/src/state/counters.ts +121 -0
  15. package/templates/counter/tsconfig.json +16 -0
  16. package/templates/dashboard/README.md +95 -0
  17. package/templates/dashboard/package.json +18 -0
  18. package/templates/dashboard/src/App.ts +72 -0
  19. package/templates/dashboard/src/components/Footer.ts +102 -0
  20. package/templates/dashboard/src/components/Header.ts +108 -0
  21. package/templates/dashboard/src/components/LogsPanel.ts +98 -0
  22. package/templates/dashboard/src/components/MetricsPanel.ts +145 -0
  23. package/templates/dashboard/src/components/Sidebar.ts +162 -0
  24. package/templates/dashboard/src/components/TrafficPanel.ts +129 -0
  25. package/templates/dashboard/src/main.ts +66 -0
  26. package/templates/dashboard/src/state/logs.ts +42 -0
  27. package/templates/dashboard/src/state/metrics.ts +129 -0
  28. package/templates/dashboard/src/state/theme.ts +20 -0
  29. package/templates/dashboard/tsconfig.json +16 -0
  30. package/templates/minimal/README.md +98 -0
  31. package/templates/minimal/package.json +18 -0
  32. package/templates/minimal/src/App.ts +108 -0
  33. package/templates/minimal/src/components/Header.ts +52 -0
  34. package/templates/minimal/src/main.ts +24 -0
  35. package/templates/minimal/tsconfig.json +16 -0
  36. package/src/commands/create.ts +0 -300
  37. package/src/index.ts +0 -75
  38. package/src/utils/colors.ts +0 -132
  39. package/src/utils/prompts.ts +0 -273
package/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # @rlabs-inc/create-tui
2
+
3
+ Scaffold professional TUI Framework applications with a single command.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ # Create a new project interactively
9
+ bunx @rlabs-inc/create-tui my-app
10
+
11
+ # Or use a specific template
12
+ bunx @rlabs-inc/create-tui my-app --template counter
13
+ ```
14
+
15
+ ## Templates
16
+
17
+ ### minimal
18
+
19
+ A clean starting point with essential structure.
20
+
21
+ ```
22
+ my-app/
23
+ ├── src/
24
+ │ └── main.ts
25
+ ├── package.json
26
+ └── tsconfig.json
27
+ ```
28
+
29
+ **Features:**
30
+ - Basic fullscreen app with theme support
31
+ - Keyboard handling (quit with q/Escape)
32
+ - Ready for your components
33
+
34
+ ### counter
35
+
36
+ Interactive counter showcase demonstrating reactive state management.
37
+
38
+ ```
39
+ my-app/
40
+ ├── src/
41
+ │ ├── main.ts
42
+ │ ├── App.ts
43
+ │ ├── components/
44
+ │ │ ├── Counter.ts
45
+ │ │ ├── CounterPanel.ts
46
+ │ │ ├── Header.ts
47
+ │ │ └── HistoryPanel.ts
48
+ │ └── state/
49
+ │ └── counters.ts
50
+ ├── package.json
51
+ └── tsconfig.json
52
+ ```
53
+
54
+ **Features:**
55
+ - Fine-grained reactivity with signals
56
+ - Template primitives (`each`, `show`)
57
+ - Focus management with Tab navigation
58
+ - Auto-scroll in history panel
59
+ - Theme switching (12+ themes)
60
+ - Variant styling
61
+
62
+ ### dashboard
63
+
64
+ Full system monitor-style dashboard with multiple panels.
65
+
66
+ ```
67
+ my-app/
68
+ ├── src/
69
+ │ ├── main.ts
70
+ │ ├── App.ts
71
+ │ ├── components/
72
+ │ │ ├── Header.ts
73
+ │ │ ├── Sidebar.ts
74
+ │ │ ├── MetricsPanel.ts
75
+ │ │ ├── TrafficPanel.ts
76
+ │ │ ├── LogsPanel.ts
77
+ │ │ └── Footer.ts
78
+ │ └── state/
79
+ │ ├── metrics.ts
80
+ │ ├── logs.ts
81
+ │ └── theme.ts
82
+ ├── package.json
83
+ └── tsconfig.json
84
+ ```
85
+
86
+ **Features:**
87
+ - Complex multi-panel layout
88
+ - Real-time metric simulation
89
+ - Activity logging with levels (info, warn, error, success)
90
+ - Auto-scrolling log panel
91
+ - Flexbox layout with grow/shrink
92
+ - Theme switching
93
+
94
+ ## CLI Options
95
+
96
+ ```
97
+ Usage: create-tui <project-name> [options]
98
+
99
+ Options:
100
+ -t, --template <name> Template to use (minimal, counter, dashboard)
101
+ --skip-install Skip running bun install
102
+ -h, --help Show help
103
+ -v, --version Show version
104
+ ```
105
+
106
+ ## After Scaffolding
107
+
108
+ ```bash
109
+ cd my-app
110
+ bun run dev # Start development
111
+ ```
112
+
113
+ ## Framework Features Showcased
114
+
115
+ | Feature | minimal | counter | dashboard |
116
+ |---------|---------|---------|-----------|
117
+ | Signals & Reactivity | ✓ | ✓ | ✓ |
118
+ | Theme System | ✓ | ✓ | ✓ |
119
+ | Keyboard Handling | ✓ | ✓ | ✓ |
120
+ | Focus Management | - | ✓ | ✓ |
121
+ | `each()` Primitive | - | ✓ | ✓ |
122
+ | `show()` Primitive | - | ✓ | - |
123
+ | Auto-scroll | - | ✓ | ✓ |
124
+ | Variant Styling | - | ✓ | ✓ |
125
+ | Multi-panel Layout | - | ✓ | ✓ |
126
+
127
+ ## License
128
+
129
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env bun
2
+ // @bun
3
+
4
+ // src/cli.ts
5
+ function parseArgs(args) {
6
+ const result = {
7
+ help: false,
8
+ version: false
9
+ };
10
+ let i = 0;
11
+ while (i < args.length) {
12
+ const arg = args[i];
13
+ if (arg === "-h" || arg === "--help") {
14
+ result.help = true;
15
+ } else if (arg === "-v" || arg === "--version") {
16
+ result.version = true;
17
+ } else if (arg === "-t" || arg === "--template") {
18
+ result.template = args[++i];
19
+ } else if (arg === "--skip-install") {
20
+ result.skipInstall = true;
21
+ } else if (!arg.startsWith("-") && !result.projectName) {
22
+ result.projectName = arg;
23
+ }
24
+ i++;
25
+ }
26
+ return result;
27
+ }
28
+
29
+ // src/scaffold.ts
30
+ import { existsSync, mkdirSync, readdirSync, statSync, readFileSync, writeFileSync } from "fs";
31
+ import { join, dirname } from "path";
32
+ var {spawn } = globalThis.Bun;
33
+ async function scaffold(options) {
34
+ const { projectName, template, skipInstall } = options;
35
+ const targetDir = join(process.cwd(), projectName);
36
+ console.log();
37
+ console.log(` Creating project: ${projectName}`);
38
+ console.log(` Template: ${template}`);
39
+ console.log();
40
+ if (existsSync(targetDir)) {
41
+ const files = readdirSync(targetDir);
42
+ if (files.length > 0) {
43
+ throw new Error(`Directory "${projectName}" already exists and is not empty`);
44
+ }
45
+ } else {
46
+ mkdirSync(targetDir, { recursive: true });
47
+ }
48
+ const templatesDir = findTemplatesDir();
49
+ const templateDir = join(templatesDir, template);
50
+ if (!existsSync(templateDir)) {
51
+ throw new Error(`Template "${template}" not found at ${templateDir}`);
52
+ }
53
+ console.log(" Copying template files...");
54
+ copyDir(templateDir, targetDir, projectName);
55
+ if (!skipInstall) {
56
+ console.log(" Installing dependencies...");
57
+ await runCommand("bun", ["install"], targetDir);
58
+ }
59
+ console.log();
60
+ console.log(` Done! Your project is ready.`);
61
+ console.log();
62
+ console.log(` Next steps:`);
63
+ console.log(` cd ${projectName}`);
64
+ if (skipInstall) {
65
+ console.log(` bun install`);
66
+ }
67
+ console.log(` bun run dev`);
68
+ console.log();
69
+ }
70
+ function findTemplatesDir() {
71
+ const devPath = join(dirname(import.meta.dir), "templates");
72
+ if (existsSync(devPath)) {
73
+ return devPath;
74
+ }
75
+ const pkgPath = join(dirname(dirname(import.meta.dir)), "templates");
76
+ if (existsSync(pkgPath)) {
77
+ return pkgPath;
78
+ }
79
+ throw new Error("Could not find templates directory");
80
+ }
81
+ function copyDir(src, dest, projectName) {
82
+ const entries = readdirSync(src);
83
+ for (const entry of entries) {
84
+ const srcPath = join(src, entry);
85
+ const destPath = join(dest, entry);
86
+ const stat = statSync(srcPath);
87
+ if (stat.isDirectory()) {
88
+ mkdirSync(destPath, { recursive: true });
89
+ copyDir(srcPath, destPath, projectName);
90
+ } else {
91
+ let content = readFileSync(srcPath, "utf-8");
92
+ content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
93
+ writeFileSync(destPath, content);
94
+ }
95
+ }
96
+ }
97
+ async function runCommand(command, args, cwd) {
98
+ const proc = spawn({
99
+ cmd: [command, ...args],
100
+ cwd,
101
+ stdout: "inherit",
102
+ stderr: "inherit"
103
+ });
104
+ const exitCode = await proc.exited;
105
+ if (exitCode !== 0) {
106
+ throw new Error(`Command "${command} ${args.join(" ")}" failed with exit code ${exitCode}`);
107
+ }
108
+ }
109
+
110
+ // src/interactive.ts
111
+ import * as readline from "readline";
112
+ var TEMPLATES = ["minimal", "counter", "dashboard"];
113
+ async function runInteractive(existingName, existingTemplate) {
114
+ const rl = readline.createInterface({
115
+ input: process.stdin,
116
+ output: process.stdout
117
+ });
118
+ const question = (prompt) => {
119
+ return new Promise((resolve) => {
120
+ rl.question(prompt, (answer) => {
121
+ resolve(answer.trim());
122
+ });
123
+ });
124
+ };
125
+ console.log();
126
+ console.log(" @rlabs-inc/create-tui");
127
+ console.log(" Create TUI Framework applications");
128
+ console.log();
129
+ let projectName = existingName;
130
+ if (!projectName) {
131
+ projectName = await question(" Project name: ");
132
+ if (!projectName) {
133
+ projectName = "my-tui-app";
134
+ }
135
+ }
136
+ let template = existingTemplate;
137
+ if (!template) {
138
+ console.log();
139
+ console.log(" Available templates:");
140
+ console.log(" 1. minimal - Bare bones project structure");
141
+ console.log(" 2. counter - Classic reactive counter example");
142
+ console.log(" 3. dashboard - Multi-component layout showcase");
143
+ console.log();
144
+ const choice = await question(" Select template (1-3) [1]: ");
145
+ const index = parseInt(choice, 10) - 1;
146
+ if (index >= 0 && index < TEMPLATES.length) {
147
+ template = TEMPLATES[index];
148
+ } else {
149
+ template = "minimal";
150
+ }
151
+ }
152
+ rl.close();
153
+ return {
154
+ projectName,
155
+ template,
156
+ skipInstall: false
157
+ };
158
+ }
159
+
160
+ // src/index.ts
161
+ var TEMPLATES2 = ["minimal", "counter", "dashboard"];
162
+ async function main() {
163
+ const args = parseArgs(process.argv.slice(2));
164
+ if (args.help) {
165
+ printHelp();
166
+ process.exit(0);
167
+ }
168
+ if (args.version) {
169
+ console.log("0.2.0");
170
+ process.exit(0);
171
+ }
172
+ let options;
173
+ if (!args.projectName || !args.template) {
174
+ options = await runInteractive(args.projectName, args.template);
175
+ } else {
176
+ if (!TEMPLATES2.includes(args.template)) {
177
+ console.error(`
178
+ Error: Unknown template "${args.template}"`);
179
+ console.error(`Available templates: ${TEMPLATES2.join(", ")}
180
+ `);
181
+ process.exit(1);
182
+ }
183
+ options = {
184
+ projectName: args.projectName,
185
+ template: args.template,
186
+ skipInstall: args.skipInstall ?? false
187
+ };
188
+ }
189
+ await scaffold(options);
190
+ }
191
+ function printHelp() {
192
+ console.log(`
193
+ @rlabs-inc/create-tui
194
+
195
+ Create TUI Framework applications with professional project structure.
196
+
197
+ Usage:
198
+ bunx @rlabs-inc/create-tui [project-name] [options]
199
+
200
+ Options:
201
+ -t, --template <name> Template to use (minimal, counter, dashboard)
202
+ --skip-install Skip dependency installation
203
+ -h, --help Show this help message
204
+ -v, --version Show version number
205
+
206
+ Examples:
207
+ bunx @rlabs-inc/create-tui my-app
208
+ bunx @rlabs-inc/create-tui my-app --template counter
209
+ bunx @rlabs-inc/create-tui my-app -t dashboard --skip-install
210
+
211
+ Templates:
212
+ minimal Bare bones project structure
213
+ counter Classic reactive counter example
214
+ dashboard Multi-component layout showcase
215
+ `);
216
+ }
217
+ main().catch((err) => {
218
+ console.error(`
219
+ Error:`, err.message);
220
+ process.exit(1);
221
+ });
package/package.json CHANGED
@@ -1,16 +1,28 @@
1
1
  {
2
2
  "name": "@rlabs-inc/create-tui",
3
- "version": "0.1.6",
4
- "description": "Create TUI Framework applications - The Terminal UI Framework for TypeScript/Bun",
3
+ "version": "0.2.0",
4
+ "description": "Create TUI Framework applications with a single command",
5
5
  "type": "module",
6
6
  "bin": {
7
- "create-tui": "./src/index.ts"
7
+ "create-tui": "./dist/index.js"
8
8
  },
9
9
  "files": [
10
- "src"
10
+ "dist",
11
+ "templates"
11
12
  ],
12
13
  "scripts": {
13
- "dev": "bun run src/index.ts"
14
+ "dev": "bun run src/index.ts",
15
+ "build": "bun build src/index.ts --outfile dist/index.js --target bun",
16
+ "typecheck": "tsc --noEmit",
17
+ "prepublishOnly": "bun run build"
18
+ },
19
+ "dependencies": {
20
+ "@rlabs-inc/tui": "latest",
21
+ "@rlabs-inc/signals": "latest"
22
+ },
23
+ "devDependencies": {
24
+ "@types/bun": "latest",
25
+ "typescript": "^5.0.0"
14
26
  },
15
27
  "keywords": [
16
28
  "tui",
@@ -23,18 +35,11 @@
23
35
  "typescript",
24
36
  "reactive"
25
37
  ],
26
- "author": "Rusty & Watson",
38
+ "author": "RLabs Inc",
27
39
  "license": "MIT",
28
40
  "repository": {
29
41
  "type": "git",
30
- "url": "https://github.com/rlabs-inc/tui.git",
31
- "directory": "packages/tui-cli"
32
- },
33
- "homepage": "https://github.com/rlabs-inc/tui",
34
- "engines": {
35
- "node": ">=18"
42
+ "url": "https://github.com/rlabs-inc/create-tui"
36
43
  },
37
- "publishConfig": {
38
- "access": "public"
39
- }
44
+ "homepage": "https://github.com/rlabs-inc/tui"
40
45
  }
@@ -0,0 +1,88 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ A reactive counter showcase demonstrating TUI Framework's fine-grained reactivity.
4
+
5
+ ## Features
6
+
7
+ - Multiple independent counters with different variants
8
+ - Theme switching between 12+ built-in themes
9
+ - Keyboard navigation and focus management
10
+ - Reactive derived state (totals, averages)
11
+ - Component composition patterns
12
+
13
+ ## Getting Started
14
+
15
+ ```bash
16
+ # Install dependencies
17
+ bun install
18
+
19
+ # Run the application
20
+ bun run dev
21
+ ```
22
+
23
+ ## Controls
24
+
25
+ | Key | Action |
26
+ |-----|--------|
27
+ | `Tab` | Focus next counter |
28
+ | `Shift+Tab` | Focus previous counter |
29
+ | `+` / `=` | Increment focused counter |
30
+ | `-` | Decrement focused counter |
31
+ | `r` | Reset focused counter |
32
+ | `R` | Reset all counters |
33
+ | `t` | Cycle through themes |
34
+ | `q` / `Escape` | Quit |
35
+
36
+ ## Project Structure
37
+
38
+ ```
39
+ {{PROJECT_NAME}}/
40
+ ├── src/
41
+ │ ├── main.ts # Entry point
42
+ │ ├── App.ts # Root component with layout
43
+ │ ├── state/
44
+ │ │ └── counters.ts # Counter state management
45
+ │ └── components/
46
+ │ ├── Counter.ts # Individual counter component
47
+ │ ├── CounterPanel.ts # Panel with multiple counters
48
+ │ ├── StatsPanel.ts # Statistics display
49
+ │ ├── Header.ts # App header
50
+ │ └── KeyBindings.ts # Help panel
51
+ ├── package.json
52
+ └── README.md
53
+ ```
54
+
55
+ ## Key Concepts Demonstrated
56
+
57
+ ### Signals & Derived State
58
+
59
+ ```typescript
60
+ const counter = signal(0)
61
+ const doubled = derived(() => counter.value * 2)
62
+ ```
63
+
64
+ ### Theme Variants
65
+
66
+ ```typescript
67
+ box({
68
+ variant: 'primary', // Uses theme's primary color
69
+ children: () => { ... }
70
+ })
71
+ ```
72
+
73
+ ### Focus Management
74
+
75
+ ```typescript
76
+ import { focusManager } from '@rlabs-inc/tui'
77
+
78
+ // Register focusable component
79
+ input({ ... }) // Auto-registers
80
+
81
+ // Navigate focus
82
+ keyboard.onKey('Tab', () => focusManager.next())
83
+ ```
84
+
85
+ ## Learn More
86
+
87
+ - [TUI Framework](https://github.com/rlabs-inc/tui)
88
+ - [Signals Library](https://github.com/rlabs-inc/signals)
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "bun run src/main.ts",
7
+ "build": "bun build src/main.ts --outfile dist/main.js --target bun",
8
+ "typecheck": "tsc --noEmit"
9
+ },
10
+ "dependencies": {
11
+ "@rlabs-inc/tui": "latest",
12
+ "@rlabs-inc/signals": "latest"
13
+ },
14
+ "devDependencies": {
15
+ "@types/bun": "latest",
16
+ "typescript": "^5.0.0"
17
+ }
18
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Root Application Component
3
+ *
4
+ * Layout structure:
5
+ * - Header (app title + theme indicator)
6
+ * - Main area:
7
+ * - Left: Counter panel with focusable counters
8
+ * - Right: Stats panel + scrollable history
9
+ * - Footer (key bindings)
10
+ */
11
+
12
+ import { box, text, t, BorderStyle } from '@rlabs-inc/tui'
13
+ import { Header } from './components/Header'
14
+ import { CounterPanel } from './components/CounterPanel'
15
+ import { StatsPanel } from './components/StatsPanel'
16
+ import { HistoryPanel } from './components/HistoryPanel'
17
+ import { KeyBindings } from './components/KeyBindings'
18
+
19
+ export function App() {
20
+ box({
21
+ width: '100%',
22
+ height: '100%',
23
+ flexDirection: 'column',
24
+ children: () => {
25
+ // Header
26
+ Header()
27
+
28
+ // Main content area
29
+ box({
30
+ grow: 1,
31
+ flexDirection: 'row',
32
+ padding: 1,
33
+ gap: 1,
34
+ children: () => {
35
+ // Left panel: Counters
36
+ box({
37
+ width: '50%',
38
+ flexDirection: 'column',
39
+ gap: 1,
40
+ children: () => {
41
+ CounterPanel()
42
+ },
43
+ })
44
+
45
+ // Right panel: Stats + History
46
+ box({
47
+ grow: 1,
48
+ flexDirection: 'column',
49
+ gap: 1,
50
+ children: () => {
51
+ // Statistics
52
+ StatsPanel()
53
+
54
+ // History with auto-scroll
55
+ HistoryPanel()
56
+ },
57
+ })
58
+ },
59
+ })
60
+
61
+ // Footer: Key bindings
62
+ KeyBindings()
63
+ },
64
+ })
65
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Counter Component
3
+ *
4
+ * Individual counter with focus support and variant styling.
5
+ * Demonstrates:
6
+ * - Focus management integration
7
+ * - Variant-based theming
8
+ * - Reactive value display
9
+ */
10
+
11
+ import { derived } from '@rlabs-inc/signals'
12
+ import { box, text, t, BorderStyle, Attr, getVariantStyle } from '@rlabs-inc/tui'
13
+ import type { Counter as CounterState } from '../state/counters'
14
+
15
+ interface CounterProps {
16
+ counter: CounterState
17
+ focused: boolean
18
+ }
19
+
20
+ export function Counter({ counter, focused }: CounterProps) {
21
+ // Get variant colors
22
+ const variantStyle = getVariantStyle(counter.variant)
23
+
24
+ box({
25
+ border: focused ? BorderStyle.DOUBLE : BorderStyle.SINGLE,
26
+ borderColor: focused ? t.primary : t.border,
27
+ padding: 1,
28
+ bg: focused ? t.surface : undefined,
29
+ children: () => {
30
+ // Counter name with variant indicator
31
+ box({
32
+ flexDirection: 'row',
33
+ justifyContent: 'space-between',
34
+ children: () => {
35
+ text({
36
+ content: counter.name,
37
+ fg: focused ? t.primary : t.text,
38
+ attrs: focused ? Attr.BOLD : 0,
39
+ })
40
+ box({
41
+ width: 2,
42
+ height: 1,
43
+ bg: variantStyle.bg,
44
+ })
45
+ },
46
+ })
47
+
48
+ // Value display - large and centered
49
+ box({
50
+ alignItems: 'center',
51
+ padding: 1,
52
+ children: () => {
53
+ text({
54
+ content: derived(() => {
55
+ const val = counter.value.value
56
+ return val >= 0 ? `+${val}` : `${val}`
57
+ }),
58
+ fg: derived(() =>
59
+ counter.value.value >= 0 ? t.success.value : t.error.value
60
+ ),
61
+ attrs: Attr.BOLD,
62
+ })
63
+ },
64
+ })
65
+
66
+ // Focus indicator
67
+ box({
68
+ alignItems: 'center',
69
+ children: () => {
70
+ text({
71
+ content: focused ? '[+/-] to change' : '',
72
+ fg: t.textDim,
73
+ })
74
+ },
75
+ })
76
+ },
77
+ })
78
+ }