create-termui-app 0.1.2 → 0.1.4

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/README.md CHANGED
@@ -17,23 +17,28 @@ The CLI walks you through a few choices, then generates a working project.
17
17
 
18
18
  | Template | What you get |
19
19
  |----------|-------------|
20
- | Empty | One file, no dependencies beyond core. Start from scratch |
21
- | Dashboard | Real-time gauges, tables, and a status bar |
22
- | Interactive Tool | Forms, selects, prompts. Good for CLI wizards |
20
+ | Empty | One file, no dependencies beyond core. Start fresh |
21
+ | Dashboard | Real-time gauges, tables, a status bar, and Grid layout |
22
+ | Interactive Tool | Forms, selects, and prompts. Good for CLI wizards |
23
23
  | CLI Wrapper | Wraps an existing shell command in a TermUI interface |
24
24
 
25
+ All templates include:
26
+ - `AutoThemeProvider` at the app root for automatic theme detection
27
+ - `ErrorBoundary` wrapping for crash recovery
28
+ - `useKeymap` for all key handling instead of raw `useInput`
29
+
25
30
  ## Themes
26
31
 
27
- Choose one of five built-in themes during setup: Default, Cyberpunk, Nord, Dracula, or Catppuccin. You can change it later in `termui.config.ts`.
32
+ Choose one of six built-in themes during setup: Default, Cyberpunk, Nord, Dracula, Catppuccin, or Solarized. Change it later in `termui.config.ts` or switch at runtime with `useTheme()`.
28
33
 
29
34
  ## Optional features
30
35
 
31
36
  The CLI asks which extras to include:
32
37
 
33
- - **Screen Router** file-based navigation between screens
34
- - **Data Providers** CPU, memory, disk monitoring out of the box
35
- - **Hot Reload** auto-restart on save via `@termuijs/dev-server`
36
- - **Testing** Vitest config with `@termuijs/testing` ready to go
38
+ - **Screen Router** - File-based navigation between screens
39
+ - **Data Providers** - CPU, memory, disk monitoring via `@termuijs/data` hooks
40
+ - **Hot Reload** - Auto-restart on save via `@termuijs/dev-server`
41
+ - **Testing** - Vitest config with `@termuijs/testing` ready to go
37
42
 
38
43
  ## Generated project
39
44
 
@@ -48,7 +53,51 @@ my-app/
48
53
  index.tsx
49
54
  ```
50
55
 
51
- Everything is TypeScript, and the dev server is preconfigured in the `dev` script.
56
+ Everything is TypeScript. The dev server is preconfigured in the `dev` script.
57
+
58
+ ## Modern patterns in generated code
59
+
60
+ All templates use current TermUI patterns:
61
+
62
+ ```tsx
63
+ // Generated entry file example (dashboard template)
64
+ import { AutoThemeProvider, ErrorBoundary } from '@termuijs/jsx'
65
+ import { useKeymap } from '@termuijs/jsx'
66
+ import { useCpu, useMemory } from '@termuijs/data'
67
+ import { useNotifications } from '@termuijs/ui'
68
+
69
+ function App() {
70
+ const { notify } = useNotifications()
71
+
72
+ useKeymap({
73
+ 'ctrl+c': () => process.exit(0),
74
+ 'r': () => notify('Refreshed', { type: 'success' }),
75
+ })
76
+
77
+ const cpu = useCpu(1000)
78
+ const mem = useMemory(1000)
79
+
80
+ return (
81
+ <Box flexDirection="column" gap={1}>
82
+ <Text bold>System Monitor</Text>
83
+ <gauge label="CPU" value={cpu.usage / 100} />
84
+ <gauge label="MEM" value={mem.used / mem.total} />
85
+ </Box>
86
+ )
87
+ }
88
+
89
+ render(
90
+ <AutoThemeProvider fallback="dracula">
91
+ <ErrorBoundary fallback={(e) => <Text color="red">{e.message}</Text>}>
92
+ <App />
93
+ </ErrorBoundary>
94
+ </AutoThemeProvider>
95
+ )
96
+ ```
97
+
98
+ ## Documentation
99
+
100
+ Full docs at [www.termui.io/docs/getting-started/installation](https://www.termui.io/docs/getting-started/installation).
52
101
 
53
102
  ## License
54
103
 
package/dist/index.js CHANGED
@@ -126,94 +126,374 @@ export default defineConfig({
126
126
  function generateEmptyTemplate(config) {
127
127
  return [{
128
128
  path: "src/index.tsx",
129
- content: `import { app, text } from '@termuijs/quick';
130
-
131
- app('${config.name}')
132
- .rows(
133
- text('Hello from TermUI! Edit src/index.tsx to get started.'),
134
- )
135
- .keys({ q: 'quit' })
136
- .run();
129
+ content: `/** @jsxImportSource @termuijs/jsx */
130
+ import { render, useState, useKeymap, ErrorBoundary } from '@termuijs/jsx';
131
+ import { AutoThemeProvider } from '@termuijs/tss';
132
+
133
+ function App() {
134
+ const [count, setCount] = useState(0);
135
+
136
+ useKeymap([
137
+ { key: 'q', action: () => process.exit(0), description: 'Quit' },
138
+ { key: 'c', ctrl: true, action: () => process.exit(0), description: 'Quit' },
139
+ { key: '+', action: () => setCount(c => c + 1), description: 'Increment' },
140
+ { key: '-', action: () => setCount(c => Math.max(0, c - 1)), description: 'Decrement' },
141
+ ]);
142
+
143
+ return (
144
+ <AutoThemeProvider>
145
+ <ErrorBoundary fallback={(err) => <text color="red">{err.message}</text>}>
146
+ <box border="single" padding={1}>
147
+ <text bold>Welcome to ${config.name}!</text>
148
+ <text>Edit src/index.tsx to get started.</text>
149
+ <text>Count: {count} (+/- to change, q to quit)</text>
150
+ </box>
151
+ </ErrorBoundary>
152
+ </AutoThemeProvider>
153
+ );
154
+ }
155
+
156
+ render(<App />, { title: '${config.name}' });
137
157
  `
138
158
  }];
139
159
  }
140
160
  function generateDashboardTemplate(config) {
141
161
  return [{
142
162
  path: "src/index.tsx",
143
- content: `import { app, gauge, table, sparkline } from '@termuijs/quick';
144
- ${config.features.dataProviders ? "import { cpu, memory, disk, processes } from '@termuijs/data';" : ""}
145
-
146
- // Sample data for demo
147
- let cpuVal = 0.45;
148
- let memVal = 0.62;
149
- let dskVal = 0.38;
150
-
151
- app('\u26A1 ${config.name}')
152
- .rows(
153
- // Row 1: Gauges
154
- gauge('CPU', () => ${config.features.dataProviders ? "cpu.percent / 100" : "cpuVal"}),
155
- gauge('MEM', () => ${config.features.dataProviders ? "memory.percent / 100" : "memVal"}),
156
- gauge('DSK', () => ${config.features.dataProviders ? "disk.percent / 100" : "dskVal"}),
157
- // Row 2: Table
158
- table('Processes',
159
- () => ${config.features.dataProviders ? `processes.top(10).map(p => ({
160
- Name: p.name.slice(0, 18),
161
- PID: p.pid,
162
- 'CPU%': p.cpu.toFixed(1),
163
- 'MEM%': p.mem.toFixed(1),
164
- }))` : `[
165
- { Name: 'node', PID: 1234, 'CPU%': '5.0', 'MEM%': '2.1' },
166
- { Name: 'chrome', PID: 5678, 'CPU%': '12.3', 'MEM%': '8.4' },
167
- ]`},
168
- ['Name', 'PID', 'CPU%', 'MEM%'],
169
- ),
170
- )
171
- .keys({ q: 'quit', r: 'refresh' })
172
- .refresh('1s')
173
- .run();
163
+ content: `/** @jsxImportSource @termuijs/jsx */
164
+ import { render, useState, useEffect, useKeymap, ErrorBoundary } from '@termuijs/jsx';
165
+ import { AutoThemeProvider, useTheme } from '@termuijs/tss';
166
+ ${config.features.dataProviders ? "import { useCpu, useMemory, useDisk } from '@termuijs/data';" : ""}
167
+
168
+ // \u2500\u2500 Sample static data (replace with live hooks when dataProviders = true) \u2500\u2500
169
+ ${config.features.dataProviders ? "" : `const SAMPLE_PROCS = [
170
+ { Name: 'node', PID: 1234, 'CPU%': '5.0', 'MEM%': '2.1' },
171
+ { Name: 'chrome', PID: 5678, 'CPU%': '12.3', 'MEM%': '8.4' },
172
+ { Name: 'bash', PID: 9012, 'CPU%': '0.1', 'MEM%': '0.3' },
173
+ ];`}
174
+
175
+ function GaugeRow({ label, value }: { label: string; value: number }) {
176
+ const theme = useTheme();
177
+ const filled = Math.round(value * 20);
178
+ const empty = 20 - filled;
179
+ const bar = '[' + '#'.repeat(filled) + '-'.repeat(empty) + ']';
180
+ return (
181
+ <row gap={2}>
182
+ <text color={theme.colors.primary}>{label.padEnd(4)}</text>
183
+ <text>{bar}</text>
184
+ <text>{(value * 100).toFixed(1).padStart(5)}%</text>
185
+ </row>
186
+ );
187
+ }
188
+
189
+ function Dashboard() {
190
+ const [tick, setTick] = useState(0);
191
+ ${config.features.dataProviders ? ` const cpu = useCpu();
192
+ const mem = useMemory();
193
+ const disk = useDisk();
194
+ const cpuVal = (cpu.percent ?? 0) / 100;
195
+ const memVal = (mem.percent ?? 0) / 100;
196
+ const diskVal = (disk.percent ?? 0) / 100;` : ` const [cpuVal, setCpuVal] = useState(0.45);
197
+ const [memVal, setMemVal] = useState(0.62);
198
+ const [diskVal, setDiskVal] = useState(0.38);
199
+
200
+ // Simulate live updates
201
+ useEffect(() => {
202
+ const id = setInterval(() => {
203
+ setCpuVal(v => Math.min(1, Math.max(0, v + (Math.random() - 0.5) * 0.05)));
204
+ setMemVal(v => Math.min(1, Math.max(0, v + (Math.random() - 0.5) * 0.02)));
205
+ setDiskVal(v => Math.min(1, Math.max(0, v + (Math.random() - 0.5) * 0.01)));
206
+ setTick(t => t + 1);
207
+ }, 1000);
208
+ return () => clearInterval(id);
209
+ }, []);`}
210
+
211
+ useKeymap([
212
+ { key: 'q', action: () => process.exit(0), description: 'Quit' },
213
+ { key: 'c', ctrl: true, action: () => process.exit(0), description: 'Quit' },
214
+ { key: 'r', action: () => setTick(t => t + 1), description: 'Refresh' },
215
+ ]);
216
+
217
+ const theme = useTheme();
218
+
219
+ return (
220
+ <box flexDirection="column" padding={1}>
221
+ <text bold color={theme.colors.primary}>${config.name} Dashboard</text>
222
+ <divider />
223
+
224
+ <grid columns={12} gap={1}>
225
+ {/* Gauges \u2014 top row */}
226
+ <box width="100%" flexDirection="column" border="single" padding={1} flexGrow={4}>
227
+ <text bold>System Resources</text>
228
+ <GaugeRow label="CPU" value={cpuVal} />
229
+ <GaugeRow label="MEM" value={memVal} />
230
+ <GaugeRow label="DISK" value={diskVal} />
231
+ </box>
232
+
233
+ {/* Info panel */}
234
+ <box width="100%" flexDirection="column" border="single" padding={1} flexGrow={8}>
235
+ <text bold>Process Summary</text>
236
+ <text color={theme.colors.muted}>Press r to refresh, q to quit</text>
237
+ <text>Tick: {tick}</text>
238
+ ${config.features.dataProviders ? ` <skeleton variant="shimmer" />` : ` <text>node PID:1234 CPU: {(cpuVal * 100).toFixed(1)}%</text>
239
+ <text>chrome PID:5678 MEM: {(memVal * 100).toFixed(1)}%</text>`}
240
+ </box>
241
+ </grid>
242
+ </box>
243
+ );
244
+ }
245
+
246
+ function App() {
247
+ return (
248
+ <AutoThemeProvider>
249
+ <ErrorBoundary fallback={(err) => (
250
+ <box border="single" borderColor="red" padding={1}>
251
+ <text color="red" bold>Dashboard Error</text>
252
+ <text>{err.message}</text>
253
+ </box>
254
+ )}>
255
+ <Dashboard />
256
+ </ErrorBoundary>
257
+ </AutoThemeProvider>
258
+ );
259
+ }
260
+
261
+ render(<App />, { title: '${config.name}' });
174
262
  `
175
263
  }];
176
264
  }
177
265
  function generateInteractiveTemplate(config) {
178
266
  return [{
179
267
  path: "src/index.tsx",
180
- content: `import { app, text, list, input } from '@termuijs/quick';
181
-
182
- const items = ['Option 1', 'Option 2', 'Option 3'];
183
-
184
- app('\u{1F527} ${config.name}')
185
- .rows(
186
- text('Select an option or type a command:'),
187
- list(() => items),
188
- input('Type here...', {
189
- onSubmit: (value) => items.push(value),
190
- }),
191
- )
192
- .keys({ q: 'quit' })
193
- .run();
268
+ content: `/** @jsxImportSource @termuijs/jsx */
269
+ import { render, useState, useKeymap, useRef, ErrorBoundary } from '@termuijs/jsx';
270
+ import { AutoThemeProvider, useTheme } from '@termuijs/tss';
271
+ import { caps } from '@termuijs/core';
272
+
273
+ // ASCII-safe symbols
274
+ const CHECK = caps.unicode ? '\u2713' : 'v';
275
+ const BULLET = caps.unicode ? '\u203A' : '>';
276
+ const SEP = caps.unicode ? '\u2500'.repeat(40) : '-'.repeat(40);
277
+
278
+ const INITIAL_ITEMS = ['Option A', 'Option B', 'Option C'];
279
+
280
+ function InteractiveTool() {
281
+ const [items, setItems] = useState<string[]>(INITIAL_ITEMS);
282
+ const [selected, setSelected] = useState(0);
283
+ const [input, setInput] = useState('');
284
+ const [done, setDone] = useState<string[]>([]);
285
+ const theme = useTheme();
286
+
287
+ useKeymap([
288
+ { key: 'q', action: () => process.exit(0), description: 'Quit' },
289
+ { key: 'c', ctrl: true, action: () => process.exit(0), description: 'Quit' },
290
+ { key: 'ArrowUp', action: () => setSelected(s => Math.max(0, s - 1)), description: 'Move up' },
291
+ { key: 'ArrowDown', action: () => setSelected(s => Math.min(items.length - 1, s + 1)), description: 'Move down' },
292
+ { key: 'k', action: () => setSelected(s => Math.max(0, s - 1)), description: 'Move up (vim)' },
293
+ { key: 'j', action: () => setSelected(s => Math.min(items.length - 1, s + 1)), description: 'Move down (vim)' },
294
+ { key: 'Enter', action: () => {
295
+ const item = items[selected];
296
+ if (item) setDone(d => d.includes(item) ? d.filter(x => x !== item) : [...d, item]);
297
+ }, description: 'Toggle selected' },
298
+ { key: 'Backspace', action: () => setInput(v => v.slice(0, -1)), description: 'Delete char' },
299
+ { key: 'n', action: () => {
300
+ if (input.trim()) {
301
+ setItems(prev => [...prev, input.trim()]);
302
+ setInput('');
303
+ }
304
+ }, description: 'Add new item' },
305
+ ]);
306
+
307
+ return (
308
+ <box flexDirection="column" padding={1}>
309
+ <text bold color={theme.colors.primary}>${config.name}</text>
310
+ <text color={theme.colors.muted}>j/k or arrows: navigate | Enter: toggle | n: add | q: quit</text>
311
+ <text>{SEP}</text>
312
+
313
+ <box flexDirection="column">
314
+ {items.map((item, i) => (
315
+ <row key={item} gap={1}>
316
+ <text color={i === selected ? theme.colors.primary : undefined}>
317
+ {i === selected ? BULLET : ' '}
318
+ </text>
319
+ <text color={done.includes(item) ? theme.colors.success : undefined}>
320
+ {done.includes(item) ? CHECK + ' ' : ' '}{item}
321
+ </text>
322
+ </row>
323
+ ))}
324
+ </box>
325
+
326
+ <text>{SEP}</text>
327
+ <row gap={1}>
328
+ <text color={theme.colors.muted}>New item:</text>
329
+ <text>{input}_</text>
330
+ </row>
331
+ <text color={theme.colors.muted} dim>Type letters then press n to add</text>
332
+ </box>
333
+ );
334
+ }
335
+
336
+ function App() {
337
+ return (
338
+ <AutoThemeProvider>
339
+ <ErrorBoundary fallback={(err) => (
340
+ <box border="single" borderColor="red" padding={1}>
341
+ <text color="red" bold>Error</text>
342
+ <text>{err.message}</text>
343
+ </box>
344
+ )}>
345
+ <InteractiveTool />
346
+ </ErrorBoundary>
347
+ </AutoThemeProvider>
348
+ );
349
+ }
350
+
351
+ render(<App />, { title: '${config.name}' });
194
352
  `
195
353
  }];
196
354
  }
197
355
  function generateCliWrapperTemplate(config) {
198
356
  return [{
199
357
  path: "src/index.tsx",
200
- content: `import { app, text, logView } from '@termuijs/quick';
201
-
202
- // CLI wrapper \u2014 displays log output
203
- const logs: string[] = [
204
- 'INFO Application started',
205
- 'INFO Waiting for input...',
206
- 'DEBUG Press q to quit',
207
- ];
208
-
209
- app('\u{1F4DF} ${config.name}')
210
- .rows(
211
- text('Command output:'),
212
- logView(() => logs),
213
- )
214
- .keys({ q: 'quit' })
215
- .refresh('1s')
216
- .run();
358
+ content: `/** @jsxImportSource @termuijs/jsx */
359
+ import { render, useState, useEffect, useKeymap, ErrorBoundary } from '@termuijs/jsx';
360
+ import { AutoThemeProvider, useTheme } from '@termuijs/tss';
361
+ import { caps } from '@termuijs/core';
362
+ import { spawn } from 'node:child_process';
363
+
364
+ // ASCII-safe symbols for terminals without full unicode support
365
+ const ICON_RUN = caps.unicode ? '>' : '>';
366
+ const ICON_DONE = caps.unicode ? '*' : '*';
367
+ const ICON_ERR = caps.unicode ? '!' : '!';
368
+ const SEP = '-'.repeat(60);
369
+
370
+ type LogLevel = 'info' | 'debug' | 'error' | 'warn';
371
+ interface LogLine {
372
+ level: LogLevel;
373
+ text: string;
374
+ ts: number;
375
+ }
376
+
377
+ function levelColor(level: LogLevel): string {
378
+ switch (level) {
379
+ case 'info': return 'green';
380
+ case 'debug': return 'cyan';
381
+ case 'warn': return 'yellow';
382
+ case 'error': return 'red';
383
+ }
384
+ }
385
+
386
+ function CliWrapper() {
387
+ const [logs, setLogs] = useState<LogLine[]>([
388
+ { level: 'info', text: 'Application started', ts: Date.now() },
389
+ { level: 'debug', text: 'Press r to re-run, q to quit', ts: Date.now() },
390
+ ]);
391
+ const [running, setRunning] = useState(false);
392
+ const [exitCode, setExitCode] = useState<number | null>(null);
393
+ const procRef = useRef<any>(null);
394
+ const theme = useTheme();
395
+
396
+ const addLog = (level: LogLevel, text: string) =>
397
+ setLogs(prev => [...prev.slice(-200), { level, text, ts: Date.now() }]);
398
+
399
+ // Example: run 'echo hello' \u2014 replace with your real command
400
+ const runCommand = () => {
401
+ if (running) return;
402
+ setRunning(true);
403
+ setExitCode(null);
404
+ addLog('info', \`\${ICON_RUN} Running command...\`);
405
+
406
+ const proc = spawn('echo', ['Hello from CLI wrapper!']);
407
+ procRef.current = proc;
408
+ proc.stdout.on('data', (d: Buffer) => {
409
+ for (const line of d.toString().split('\\n').filter(Boolean)) {
410
+ addLog('info', line);
411
+ }
412
+ });
413
+ proc.stderr.on('data', (d: Buffer) => {
414
+ for (const line of d.toString().split('\\n').filter(Boolean)) {
415
+ addLog('error', line);
416
+ }
417
+ });
418
+ proc.on('close', (code: number | null) => {
419
+ setRunning(false);
420
+ setExitCode(code);
421
+ addLog(code === 0 ? 'info' : 'error',
422
+ \`\${code === 0 ? ICON_DONE : ICON_ERR} Process exited with code \${code ?? 'null'}\`);
423
+ procRef.current = null;
424
+ });
425
+ };
426
+
427
+ // Auto-run on mount, kill process on unmount
428
+ useEffect(() => {
429
+ runCommand();
430
+ return () => {
431
+ if (procRef.current) {
432
+ procRef.current.kill();
433
+ procRef.current = null;
434
+ }
435
+ };
436
+ }, []);
437
+
438
+ useKeymap([
439
+ { key: 'q', action: () => process.exit(0), description: 'Quit' },
440
+ { key: 'c', ctrl: true, action: () => process.exit(0), description: 'Quit' },
441
+ { key: 'r', action: runCommand, description: 'Re-run command' },
442
+ { key: 'l', action: () => setLogs([]), description: 'Clear logs' },
443
+ ]);
444
+
445
+ return (
446
+ <box flexDirection="column" padding={1}>
447
+ <row gap={2}>
448
+ <text bold color={theme.colors.primary}>${config.name}</text>
449
+ <text color={running ? theme.colors.warning : theme.colors.muted}>
450
+ {running ? 'Running...' : exitCode === null ? 'Ready' : \`Exit: \${exitCode}\`}
451
+ </text>
452
+ <spacer />
453
+ <text color={theme.colors.muted}>r: re-run | l: clear | q: quit</text>
454
+ </row>
455
+ <text>{SEP}</text>
456
+
457
+ <box flexDirection="column" flexGrow={1}>
458
+ {logs.map((line, i) => (
459
+ <row key={i} gap={1}>
460
+ <text color={theme.colors.muted} dim>
461
+ {new Date(line.ts).toLocaleTimeString()}
462
+ </text>
463
+ <text color={levelColor(line.level)} bold>
464
+ {line.level.toUpperCase().padEnd(5)}
465
+ </text>
466
+ <text>{line.text}</text>
467
+ </row>
468
+ ))}
469
+ </box>
470
+
471
+ {!caps.color && (
472
+ <text color="yellow" dim>
473
+ Note: running in a terminal without color support (TERM={process.env.TERM ?? 'unset'})
474
+ </text>
475
+ )}
476
+ </box>
477
+ );
478
+ }
479
+
480
+ function App() {
481
+ return (
482
+ <AutoThemeProvider>
483
+ <ErrorBoundary fallback={(err) => (
484
+ <box border="single" borderColor="red" padding={1}>
485
+ <text color="red" bold>CLI Wrapper Error</text>
486
+ <text>{err.message}</text>
487
+ <text color="yellow">Check that the command exists and is executable.</text>
488
+ </box>
489
+ )}>
490
+ <CliWrapper />
491
+ </ErrorBoundary>
492
+ </AutoThemeProvider>
493
+ );
494
+ }
495
+
496
+ render(<App />, { title: '${config.name}' });
217
497
  `
218
498
  }];
219
499
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/prompts.ts","../src/templates.ts"],"sourcesContent":["// ─────────────────────────────────────────────────────\n// create-termui-app — Interactive CLI scaffolding tool\n// ─────────────────────────────────────────────────────\n\nimport { resolve, join } from 'node:path';\nimport { mkdirSync, writeFileSync, existsSync } from 'node:fs';\nimport { getBuiltinThemeNames } from '@termuijs/tss';\nimport { textPrompt, selectPrompt, multiSelectPrompt } from './prompts.js';\nimport { generateProject, type ProjectConfig } from './templates.js';\n\nconst TEMPLATES = ['Empty (start from scratch)', 'Dashboard (real-time data)', 'Interactive Tool (forms, prompts)', 'CLI Wrapper (wrap existing CLI)'];\nconst TEMPLATE_KEYS = ['empty', 'dashboard', 'interactive-tool', 'cli-wrapper'] as const;\nconst FEATURES = ['Screen Router', 'Data Providers', 'Hot Reload'];\n\nasync function main() {\n console.log();\n console.log(' ┌──────────────────────────────────┐');\n console.log(' │ create-termui-app │');\n console.log(' │ The React/Next.js for CLI apps │');\n console.log(' └──────────────────────────────────┘');\n console.log();\n\n // ── Get project name from args or prompt ──\n let projectName = process.argv[2];\n if (!projectName) {\n projectName = await textPrompt('Project name', 'my-termui-app');\n }\n\n // ── Template selection ──\n const templateIdx = await selectPrompt('What kind of app?', TEMPLATES);\n const template = TEMPLATE_KEYS[templateIdx];\n\n // ── Theme selection ──\n const themes = getBuiltinThemeNames();\n const themeIdx = await selectPrompt('Choose a theme', themes.map(t => t.charAt(0).toUpperCase() + t.slice(1)));\n const theme = themes[themeIdx];\n\n // ── Feature selection ──\n const featureDefaults = [false, template === 'dashboard', true]; // Router off, Data on for dashboard, HotReload on\n const featureFlags = await multiSelectPrompt('Features to include', FEATURES, featureDefaults);\n\n const config: ProjectConfig = {\n name: projectName,\n template,\n theme,\n features: {\n router: featureFlags[0],\n dataProviders: featureFlags[1],\n hotReload: featureFlags[2],\n },\n };\n\n // ── Generate project ──\n const projectDir = resolve(process.cwd(), projectName);\n if (existsSync(projectDir)) {\n console.log(`\\n ⚠ Directory \"${projectName}\" already exists. Files may be overwritten.\\n`);\n }\n\n console.log(`\\n Creating ${projectName}...`);\n\n const files = generateProject(config);\n\n for (const file of files) {\n const fullPath = join(projectDir, file.path);\n const dir = fullPath.substring(0, fullPath.lastIndexOf('/'));\n mkdirSync(dir, { recursive: true });\n writeFileSync(fullPath, file.content, 'utf-8');\n console.log(` ✓ ${file.path}`);\n }\n\n console.log();\n console.log(' ┌──────────────────────────────────┐');\n console.log(' │ ✅ Project created successfully! │');\n console.log(' └──────────────────────────────────┘');\n console.log();\n console.log(` Next steps:`);\n console.log(` cd ${projectName}`);\n console.log(` npm install`);\n console.log(` npm run dev`);\n console.log();\n}\n\nmain().catch(err => {\n console.error('Error:', err.message);\n process.exit(1);\n});\n","// ─────────────────────────────────────────────────────\n// Minimal interactive prompts (no external deps)\n// ─────────────────────────────────────────────────────\n\nimport { createInterface } from 'node:readline';\n\nconst rl = () => createInterface({ input: process.stdin, output: process.stdout });\n\nexport async function textPrompt(question: string, defaultValue?: string): Promise<string> {\n return new Promise(resolve => {\n const r = rl();\n const suffix = defaultValue ? ` (${defaultValue})` : '';\n r.question(` ${question}${suffix}: `, answer => {\n r.close();\n resolve(answer.trim() || defaultValue || '');\n });\n });\n}\n\nexport async function selectPrompt(question: string, options: string[]): Promise<number> {\n console.log(`\\n ${question}`);\n for (let i = 0; i < options.length; i++) {\n console.log(` ${i + 1}) ${options[i]}`);\n }\n const answer = await textPrompt('Enter number', '1');\n const idx = parseInt(answer) - 1;\n return Math.max(0, Math.min(idx, options.length - 1));\n}\n\nexport async function confirmPrompt(question: string, defaultValue = true): Promise<boolean> {\n const suffix = defaultValue ? '(Y/n)' : '(y/N)';\n const answer = await textPrompt(`${question} ${suffix}`);\n if (!answer) return defaultValue;\n return answer.toLowerCase().startsWith('y');\n}\n\nexport async function multiSelectPrompt(question: string, options: string[], defaults: boolean[] = []): Promise<boolean[]> {\n console.log(`\\n ${question} (comma-separated numbers, or 'all')`);\n for (let i = 0; i < options.length; i++) {\n const def = defaults[i] ? '✓' : ' ';\n console.log(` ${i + 1}) [${def}] ${options[i]}`);\n }\n const answer = await textPrompt('Enter numbers', defaults.some(d => d) ? 'keep defaults' : 'all');\n if (answer === 'all') return options.map(() => true);\n if (answer === 'keep defaults' || answer === '') return defaults.length ? defaults : options.map(() => true);\n const selected = answer.split(',').map(s => parseInt(s.trim()) - 1);\n return options.map((_, i) => selected.includes(i));\n}\n","// ─────────────────────────────────────────────────────\n// Project Templates — generates files for new apps\n// ─────────────────────────────────────────────────────\n\nimport { getBuiltinTheme } from '@termuijs/tss';\n\nexport interface ProjectConfig {\n name: string;\n template: 'empty' | 'dashboard' | 'interactive-tool' | 'cli-wrapper';\n theme: string;\n features: {\n router: boolean;\n dataProviders: boolean;\n hotReload: boolean;\n };\n}\n\nexport interface GeneratedFile {\n path: string;\n content: string;\n}\n\nexport function generateProject(config: ProjectConfig): GeneratedFile[] {\n const files: GeneratedFile[] = [];\n\n // ── package.json ──\n files.push({\n path: 'package.json',\n content: JSON.stringify({\n name: config.name,\n version: '0.1.0',\n private: true,\n type: 'module',\n scripts: {\n dev: 'tsx --watch src/index.tsx',\n build: 'tsup src/index.tsx --format esm',\n start: 'node dist/index.js',\n },\n dependencies: {\n '@termuijs/core': 'latest',\n '@termuijs/widgets': 'latest',\n '@termuijs/ui': 'latest',\n '@termuijs/jsx': 'latest',\n '@termuijs/tss': 'latest',\n '@termuijs/quick': 'latest',\n '@termuijs/motion': 'latest',\n ...(config.features.dataProviders ? { '@termuijs/data': 'latest' } : {}),\n ...(config.features.router ? { '@termuijs/router': 'latest' } : {}),\n },\n devDependencies: {\n tsx: '^4.0.0',\n tsup: '^8.0.0',\n typescript: '^5.3.0',\n },\n }, null, 2) + '\\n',\n });\n\n // ── tsconfig.json ──\n files.push({\n path: 'tsconfig.json',\n content: JSON.stringify({\n compilerOptions: {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n jsx: 'react-jsx',\n jsxImportSource: '@termuijs/jsx',\n strict: true,\n esModuleInterop: true,\n outDir: 'dist',\n rootDir: 'src',\n },\n include: ['src'],\n }, null, 2) + '\\n',\n });\n\n // ── termui.config.ts ──\n files.push({\n path: 'termui.config.ts',\n content: `import { defineConfig } from '@termuijs/core';\n\nexport default defineConfig({\n theme: '${config.theme}',\n ${config.features.hotReload ? \"hotReload: true,\" : ''}\n ${config.features.router ? \"router: { dir: './screens' },\" : ''}\n});\n`,\n });\n\n // ── Theme file ──\n const themeSrc = getBuiltinTheme(config.theme);\n if (themeSrc) {\n files.push({ path: `themes/${config.theme}.tss`, content: themeSrc.trim() + '\\n' });\n }\n\n // ── Template-specific files ──\n switch (config.template) {\n case 'dashboard':\n files.push(...generateDashboardTemplate(config));\n break;\n case 'interactive-tool':\n files.push(...generateInteractiveTemplate(config));\n break;\n case 'cli-wrapper':\n files.push(...generateCliWrapperTemplate(config));\n break;\n default:\n files.push(...generateEmptyTemplate(config));\n }\n\n return files;\n}\n\nfunction generateEmptyTemplate(config: ProjectConfig): GeneratedFile[] {\n return [{\n path: 'src/index.tsx',\n content: `import { app, text } from '@termuijs/quick';\n\napp('${config.name}')\n .rows(\n text('Hello from TermUI! Edit src/index.tsx to get started.'),\n )\n .keys({ q: 'quit' })\n .run();\n`,\n }];\n}\n\nfunction generateDashboardTemplate(config: ProjectConfig): GeneratedFile[] {\n return [{\n path: 'src/index.tsx',\n content: `import { app, gauge, table, sparkline } from '@termuijs/quick';\n${config.features.dataProviders ? \"import { cpu, memory, disk, processes } from '@termuijs/data';\" : ''}\n\n// Sample data for demo\nlet cpuVal = 0.45;\nlet memVal = 0.62;\nlet dskVal = 0.38;\n\napp('⚡ ${config.name}')\n .rows(\n // Row 1: Gauges\n gauge('CPU', () => ${config.features.dataProviders ? 'cpu.percent / 100' : 'cpuVal'}),\n gauge('MEM', () => ${config.features.dataProviders ? 'memory.percent / 100' : 'memVal'}),\n gauge('DSK', () => ${config.features.dataProviders ? 'disk.percent / 100' : 'dskVal'}),\n // Row 2: Table\n table('Processes',\n () => ${config.features.dataProviders\n ? `processes.top(10).map(p => ({\n Name: p.name.slice(0, 18),\n PID: p.pid,\n 'CPU%': p.cpu.toFixed(1),\n 'MEM%': p.mem.toFixed(1),\n }))`\n : `[\n { Name: 'node', PID: 1234, 'CPU%': '5.0', 'MEM%': '2.1' },\n { Name: 'chrome', PID: 5678, 'CPU%': '12.3', 'MEM%': '8.4' },\n ]`},\n ['Name', 'PID', 'CPU%', 'MEM%'],\n ),\n )\n .keys({ q: 'quit', r: 'refresh' })\n .refresh('1s')\n .run();\n`,\n }];\n}\n\nfunction generateInteractiveTemplate(config: ProjectConfig): GeneratedFile[] {\n return [{\n path: 'src/index.tsx',\n content: `import { app, text, list, input } from '@termuijs/quick';\n\nconst items = ['Option 1', 'Option 2', 'Option 3'];\n\napp('🔧 ${config.name}')\n .rows(\n text('Select an option or type a command:'),\n list(() => items),\n input('Type here...', {\n onSubmit: (value) => items.push(value),\n }),\n )\n .keys({ q: 'quit' })\n .run();\n`,\n }];\n}\n\nfunction generateCliWrapperTemplate(config: ProjectConfig): GeneratedFile[] {\n return [{\n path: 'src/index.tsx',\n content: `import { app, text, logView } from '@termuijs/quick';\n\n// CLI wrapper — displays log output\nconst logs: string[] = [\n 'INFO Application started',\n 'INFO Waiting for input...',\n 'DEBUG Press q to quit',\n];\n\napp('📟 ${config.name}')\n .rows(\n text('Command output:'),\n logView(() => logs),\n )\n .keys({ q: 'quit' })\n .refresh('1s')\n .run();\n`,\n }];\n}\n\n"],"mappings":"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","names":["resolve"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/prompts.ts","../src/templates.ts"],"sourcesContent":["// ─────────────────────────────────────────────────────\n// create-termui-app — Interactive CLI scaffolding tool\n// ─────────────────────────────────────────────────────\n\nimport { resolve, join } from 'node:path';\nimport { mkdirSync, writeFileSync, existsSync } from 'node:fs';\nimport { getBuiltinThemeNames } from '@termuijs/tss';\nimport { textPrompt, selectPrompt, multiSelectPrompt } from './prompts.js';\nimport { generateProject, type ProjectConfig } from './templates.js';\n\nconst TEMPLATES = ['Empty (start from scratch)', 'Dashboard (real-time data)', 'Interactive Tool (forms, prompts)', 'CLI Wrapper (wrap existing CLI)'];\nconst TEMPLATE_KEYS = ['empty', 'dashboard', 'interactive-tool', 'cli-wrapper'] as const;\nconst FEATURES = ['Screen Router', 'Data Providers', 'Hot Reload'];\n\nasync function main() {\n console.log();\n console.log(' ┌──────────────────────────────────┐');\n console.log(' │ create-termui-app │');\n console.log(' │ The React/Next.js for CLI apps │');\n console.log(' └──────────────────────────────────┘');\n console.log();\n\n // ── Get project name from args or prompt ──\n let projectName = process.argv[2];\n if (!projectName) {\n projectName = await textPrompt('Project name', 'my-termui-app');\n }\n\n // ── Template selection ──\n const templateIdx = await selectPrompt('What kind of app?', TEMPLATES);\n const template = TEMPLATE_KEYS[templateIdx];\n\n // ── Theme selection ──\n const themes = getBuiltinThemeNames();\n const themeIdx = await selectPrompt('Choose a theme', themes.map(t => t.charAt(0).toUpperCase() + t.slice(1)));\n const theme = themes[themeIdx];\n\n // ── Feature selection ──\n const featureDefaults = [false, template === 'dashboard', true]; // Router off, Data on for dashboard, HotReload on\n const featureFlags = await multiSelectPrompt('Features to include', FEATURES, featureDefaults);\n\n const config: ProjectConfig = {\n name: projectName,\n template,\n theme,\n features: {\n router: featureFlags[0],\n dataProviders: featureFlags[1],\n hotReload: featureFlags[2],\n },\n };\n\n // ── Generate project ──\n const projectDir = resolve(process.cwd(), projectName);\n if (existsSync(projectDir)) {\n console.log(`\\n ⚠ Directory \"${projectName}\" already exists. Files may be overwritten.\\n`);\n }\n\n console.log(`\\n Creating ${projectName}...`);\n\n const files = generateProject(config);\n\n for (const file of files) {\n const fullPath = join(projectDir, file.path);\n const dir = fullPath.substring(0, fullPath.lastIndexOf('/'));\n mkdirSync(dir, { recursive: true });\n writeFileSync(fullPath, file.content, 'utf-8');\n console.log(` ✓ ${file.path}`);\n }\n\n console.log();\n console.log(' ┌──────────────────────────────────┐');\n console.log(' │ ✅ Project created successfully! │');\n console.log(' └──────────────────────────────────┘');\n console.log();\n console.log(` Next steps:`);\n console.log(` cd ${projectName}`);\n console.log(` npm install`);\n console.log(` npm run dev`);\n console.log();\n}\n\nmain().catch(err => {\n console.error('Error:', err.message);\n process.exit(1);\n});\n","// ─────────────────────────────────────────────────────\n// Minimal interactive prompts (no external deps)\n// ─────────────────────────────────────────────────────\n\nimport { createInterface } from 'node:readline';\n\nconst rl = () => createInterface({ input: process.stdin, output: process.stdout });\n\nexport async function textPrompt(question: string, defaultValue?: string): Promise<string> {\n return new Promise(resolve => {\n const r = rl();\n const suffix = defaultValue ? ` (${defaultValue})` : '';\n r.question(` ${question}${suffix}: `, answer => {\n r.close();\n resolve(answer.trim() || defaultValue || '');\n });\n });\n}\n\nexport async function selectPrompt(question: string, options: string[]): Promise<number> {\n console.log(`\\n ${question}`);\n for (let i = 0; i < options.length; i++) {\n console.log(` ${i + 1}) ${options[i]}`);\n }\n const answer = await textPrompt('Enter number', '1');\n const idx = parseInt(answer) - 1;\n return Math.max(0, Math.min(idx, options.length - 1));\n}\n\nexport async function confirmPrompt(question: string, defaultValue = true): Promise<boolean> {\n const suffix = defaultValue ? '(Y/n)' : '(y/N)';\n const answer = await textPrompt(`${question} ${suffix}`);\n if (!answer) return defaultValue;\n return answer.toLowerCase().startsWith('y');\n}\n\nexport async function multiSelectPrompt(question: string, options: string[], defaults: boolean[] = []): Promise<boolean[]> {\n console.log(`\\n ${question} (comma-separated numbers, or 'all')`);\n for (let i = 0; i < options.length; i++) {\n const def = defaults[i] ? '✓' : ' ';\n console.log(` ${i + 1}) [${def}] ${options[i]}`);\n }\n const answer = await textPrompt('Enter numbers', defaults.some(d => d) ? 'keep defaults' : 'all');\n if (answer === 'all') return options.map(() => true);\n if (answer === 'keep defaults' || answer === '') return defaults.length ? defaults : options.map(() => true);\n const selected = answer.split(',').map(s => parseInt(s.trim()) - 1);\n return options.map((_, i) => selected.includes(i));\n}\n","// ─────────────────────────────────────────────────────\n// Project Templates — generates files for new apps\n// ─────────────────────────────────────────────────────\n\nimport { getBuiltinTheme } from '@termuijs/tss';\n\nexport interface ProjectConfig {\n name: string;\n template: 'empty' | 'dashboard' | 'interactive-tool' | 'cli-wrapper';\n theme: string;\n features: {\n router: boolean;\n dataProviders: boolean;\n hotReload: boolean;\n };\n}\n\nexport interface GeneratedFile {\n path: string;\n content: string;\n}\n\nexport function generateProject(config: ProjectConfig): GeneratedFile[] {\n const files: GeneratedFile[] = [];\n\n // ── package.json ──\n files.push({\n path: 'package.json',\n content: JSON.stringify({\n name: config.name,\n version: '0.1.0',\n private: true,\n type: 'module',\n scripts: {\n dev: 'tsx --watch src/index.tsx',\n build: 'tsup src/index.tsx --format esm',\n start: 'node dist/index.js',\n },\n dependencies: {\n '@termuijs/core': 'latest',\n '@termuijs/widgets': 'latest',\n '@termuijs/ui': 'latest',\n '@termuijs/jsx': 'latest',\n '@termuijs/tss': 'latest',\n '@termuijs/quick': 'latest',\n '@termuijs/motion': 'latest',\n ...(config.features.dataProviders ? { '@termuijs/data': 'latest' } : {}),\n ...(config.features.router ? { '@termuijs/router': 'latest' } : {}),\n },\n devDependencies: {\n tsx: '^4.0.0',\n tsup: '^8.0.0',\n typescript: '^5.3.0',\n },\n }, null, 2) + '\\n',\n });\n\n // ── tsconfig.json ──\n files.push({\n path: 'tsconfig.json',\n content: JSON.stringify({\n compilerOptions: {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n jsx: 'react-jsx',\n jsxImportSource: '@termuijs/jsx',\n strict: true,\n esModuleInterop: true,\n outDir: 'dist',\n rootDir: 'src',\n },\n include: ['src'],\n }, null, 2) + '\\n',\n });\n\n // ── termui.config.ts ──\n files.push({\n path: 'termui.config.ts',\n content: `import { defineConfig } from '@termuijs/core';\n\nexport default defineConfig({\n theme: '${config.theme}',\n ${config.features.hotReload ? \"hotReload: true,\" : ''}\n ${config.features.router ? \"router: { dir: './screens' },\" : ''}\n});\n`,\n });\n\n // ── Theme file ──\n const themeSrc = getBuiltinTheme(config.theme);\n if (themeSrc) {\n files.push({ path: `themes/${config.theme}.tss`, content: themeSrc.trim() + '\\n' });\n }\n\n // ── Template-specific files ──\n switch (config.template) {\n case 'dashboard':\n files.push(...generateDashboardTemplate(config));\n break;\n case 'interactive-tool':\n files.push(...generateInteractiveTemplate(config));\n break;\n case 'cli-wrapper':\n files.push(...generateCliWrapperTemplate(config));\n break;\n default:\n files.push(...generateEmptyTemplate(config));\n }\n\n return files;\n}\n\nfunction generateEmptyTemplate(config: ProjectConfig): GeneratedFile[] {\n return [{\n path: 'src/index.tsx',\n content: `/** @jsxImportSource @termuijs/jsx */\nimport { render, useState, useKeymap, ErrorBoundary } from '@termuijs/jsx';\nimport { AutoThemeProvider } from '@termuijs/tss';\n\nfunction App() {\n const [count, setCount] = useState(0);\n\n useKeymap([\n { key: 'q', action: () => process.exit(0), description: 'Quit' },\n { key: 'c', ctrl: true, action: () => process.exit(0), description: 'Quit' },\n { key: '+', action: () => setCount(c => c + 1), description: 'Increment' },\n { key: '-', action: () => setCount(c => Math.max(0, c - 1)), description: 'Decrement' },\n ]);\n\n return (\n <AutoThemeProvider>\n <ErrorBoundary fallback={(err) => <text color=\"red\">{err.message}</text>}>\n <box border=\"single\" padding={1}>\n <text bold>Welcome to ${config.name}!</text>\n <text>Edit src/index.tsx to get started.</text>\n <text>Count: {count} (+/- to change, q to quit)</text>\n </box>\n </ErrorBoundary>\n </AutoThemeProvider>\n );\n}\n\nrender(<App />, { title: '${config.name}' });\n`,\n }];\n}\n\nfunction generateDashboardTemplate(config: ProjectConfig): GeneratedFile[] {\n return [{\n path: 'src/index.tsx',\n content: `/** @jsxImportSource @termuijs/jsx */\nimport { render, useState, useEffect, useKeymap, ErrorBoundary } from '@termuijs/jsx';\nimport { AutoThemeProvider, useTheme } from '@termuijs/tss';\n${config.features.dataProviders ? \"import { useCpu, useMemory, useDisk } from '@termuijs/data';\" : ''}\n\n// ── Sample static data (replace with live hooks when dataProviders = true) ──\n${config.features.dataProviders ? '' : `const SAMPLE_PROCS = [\n { Name: 'node', PID: 1234, 'CPU%': '5.0', 'MEM%': '2.1' },\n { Name: 'chrome', PID: 5678, 'CPU%': '12.3', 'MEM%': '8.4' },\n { Name: 'bash', PID: 9012, 'CPU%': '0.1', 'MEM%': '0.3' },\n];`}\n\nfunction GaugeRow({ label, value }: { label: string; value: number }) {\n const theme = useTheme();\n const filled = Math.round(value * 20);\n const empty = 20 - filled;\n const bar = '[' + '#'.repeat(filled) + '-'.repeat(empty) + ']';\n return (\n <row gap={2}>\n <text color={theme.colors.primary}>{label.padEnd(4)}</text>\n <text>{bar}</text>\n <text>{(value * 100).toFixed(1).padStart(5)}%</text>\n </row>\n );\n}\n\nfunction Dashboard() {\n const [tick, setTick] = useState(0);\n${config.features.dataProviders\n ? ` const cpu = useCpu();\n const mem = useMemory();\n const disk = useDisk();\n const cpuVal = (cpu.percent ?? 0) / 100;\n const memVal = (mem.percent ?? 0) / 100;\n const diskVal = (disk.percent ?? 0) / 100;`\n : ` const [cpuVal, setCpuVal] = useState(0.45);\n const [memVal, setMemVal] = useState(0.62);\n const [diskVal, setDiskVal] = useState(0.38);\n\n // Simulate live updates\n useEffect(() => {\n const id = setInterval(() => {\n setCpuVal(v => Math.min(1, Math.max(0, v + (Math.random() - 0.5) * 0.05)));\n setMemVal(v => Math.min(1, Math.max(0, v + (Math.random() - 0.5) * 0.02)));\n setDiskVal(v => Math.min(1, Math.max(0, v + (Math.random() - 0.5) * 0.01)));\n setTick(t => t + 1);\n }, 1000);\n return () => clearInterval(id);\n }, []);`}\n\n useKeymap([\n { key: 'q', action: () => process.exit(0), description: 'Quit' },\n { key: 'c', ctrl: true, action: () => process.exit(0), description: 'Quit' },\n { key: 'r', action: () => setTick(t => t + 1), description: 'Refresh' },\n ]);\n\n const theme = useTheme();\n\n return (\n <box flexDirection=\"column\" padding={1}>\n <text bold color={theme.colors.primary}>${config.name} Dashboard</text>\n <divider />\n\n <grid columns={12} gap={1}>\n {/* Gauges — top row */}\n <box width=\"100%\" flexDirection=\"column\" border=\"single\" padding={1} flexGrow={4}>\n <text bold>System Resources</text>\n <GaugeRow label=\"CPU\" value={cpuVal} />\n <GaugeRow label=\"MEM\" value={memVal} />\n <GaugeRow label=\"DISK\" value={diskVal} />\n </box>\n\n {/* Info panel */}\n <box width=\"100%\" flexDirection=\"column\" border=\"single\" padding={1} flexGrow={8}>\n <text bold>Process Summary</text>\n <text color={theme.colors.muted}>Press r to refresh, q to quit</text>\n <text>Tick: {tick}</text>\n${config.features.dataProviders\n ? ` <skeleton variant=\"shimmer\" />`\n : ` <text>node PID:1234 CPU: {(cpuVal * 100).toFixed(1)}%</text>\n <text>chrome PID:5678 MEM: {(memVal * 100).toFixed(1)}%</text>`}\n </box>\n </grid>\n </box>\n );\n}\n\nfunction App() {\n return (\n <AutoThemeProvider>\n <ErrorBoundary fallback={(err) => (\n <box border=\"single\" borderColor=\"red\" padding={1}>\n <text color=\"red\" bold>Dashboard Error</text>\n <text>{err.message}</text>\n </box>\n )}>\n <Dashboard />\n </ErrorBoundary>\n </AutoThemeProvider>\n );\n}\n\nrender(<App />, { title: '${config.name}' });\n`,\n }];\n}\n\nfunction generateInteractiveTemplate(config: ProjectConfig): GeneratedFile[] {\n return [{\n path: 'src/index.tsx',\n content: `/** @jsxImportSource @termuijs/jsx */\nimport { render, useState, useKeymap, useRef, ErrorBoundary } from '@termuijs/jsx';\nimport { AutoThemeProvider, useTheme } from '@termuijs/tss';\nimport { caps } from '@termuijs/core';\n\n// ASCII-safe symbols\nconst CHECK = caps.unicode ? '✓' : 'v';\nconst BULLET = caps.unicode ? '›' : '>';\nconst SEP = caps.unicode ? '─'.repeat(40) : '-'.repeat(40);\n\nconst INITIAL_ITEMS = ['Option A', 'Option B', 'Option C'];\n\nfunction InteractiveTool() {\n const [items, setItems] = useState<string[]>(INITIAL_ITEMS);\n const [selected, setSelected] = useState(0);\n const [input, setInput] = useState('');\n const [done, setDone] = useState<string[]>([]);\n const theme = useTheme();\n\n useKeymap([\n { key: 'q', action: () => process.exit(0), description: 'Quit' },\n { key: 'c', ctrl: true, action: () => process.exit(0), description: 'Quit' },\n { key: 'ArrowUp', action: () => setSelected(s => Math.max(0, s - 1)), description: 'Move up' },\n { key: 'ArrowDown', action: () => setSelected(s => Math.min(items.length - 1, s + 1)), description: 'Move down' },\n { key: 'k', action: () => setSelected(s => Math.max(0, s - 1)), description: 'Move up (vim)' },\n { key: 'j', action: () => setSelected(s => Math.min(items.length - 1, s + 1)), description: 'Move down (vim)' },\n { key: 'Enter', action: () => {\n const item = items[selected];\n if (item) setDone(d => d.includes(item) ? d.filter(x => x !== item) : [...d, item]);\n }, description: 'Toggle selected' },\n { key: 'Backspace', action: () => setInput(v => v.slice(0, -1)), description: 'Delete char' },\n { key: 'n', action: () => {\n if (input.trim()) {\n setItems(prev => [...prev, input.trim()]);\n setInput('');\n }\n }, description: 'Add new item' },\n ]);\n\n return (\n <box flexDirection=\"column\" padding={1}>\n <text bold color={theme.colors.primary}>${config.name}</text>\n <text color={theme.colors.muted}>j/k or arrows: navigate | Enter: toggle | n: add | q: quit</text>\n <text>{SEP}</text>\n\n <box flexDirection=\"column\">\n {items.map((item, i) => (\n <row key={item} gap={1}>\n <text color={i === selected ? theme.colors.primary : undefined}>\n {i === selected ? BULLET : ' '}\n </text>\n <text color={done.includes(item) ? theme.colors.success : undefined}>\n {done.includes(item) ? CHECK + ' ' : ' '}{item}\n </text>\n </row>\n ))}\n </box>\n\n <text>{SEP}</text>\n <row gap={1}>\n <text color={theme.colors.muted}>New item:</text>\n <text>{input}_</text>\n </row>\n <text color={theme.colors.muted} dim>Type letters then press n to add</text>\n </box>\n );\n}\n\nfunction App() {\n return (\n <AutoThemeProvider>\n <ErrorBoundary fallback={(err) => (\n <box border=\"single\" borderColor=\"red\" padding={1}>\n <text color=\"red\" bold>Error</text>\n <text>{err.message}</text>\n </box>\n )}>\n <InteractiveTool />\n </ErrorBoundary>\n </AutoThemeProvider>\n );\n}\n\nrender(<App />, { title: '${config.name}' });\n`,\n }];\n}\n\nfunction generateCliWrapperTemplate(config: ProjectConfig): GeneratedFile[] {\n return [{\n path: 'src/index.tsx',\n content: `/** @jsxImportSource @termuijs/jsx */\nimport { render, useState, useEffect, useKeymap, ErrorBoundary } from '@termuijs/jsx';\nimport { AutoThemeProvider, useTheme } from '@termuijs/tss';\nimport { caps } from '@termuijs/core';\nimport { spawn } from 'node:child_process';\n\n// ASCII-safe symbols for terminals without full unicode support\nconst ICON_RUN = caps.unicode ? '>' : '>';\nconst ICON_DONE = caps.unicode ? '*' : '*';\nconst ICON_ERR = caps.unicode ? '!' : '!';\nconst SEP = '-'.repeat(60);\n\ntype LogLevel = 'info' | 'debug' | 'error' | 'warn';\ninterface LogLine {\n level: LogLevel;\n text: string;\n ts: number;\n}\n\nfunction levelColor(level: LogLevel): string {\n switch (level) {\n case 'info': return 'green';\n case 'debug': return 'cyan';\n case 'warn': return 'yellow';\n case 'error': return 'red';\n }\n}\n\nfunction CliWrapper() {\n const [logs, setLogs] = useState<LogLine[]>([\n { level: 'info', text: 'Application started', ts: Date.now() },\n { level: 'debug', text: 'Press r to re-run, q to quit', ts: Date.now() },\n ]);\n const [running, setRunning] = useState(false);\n const [exitCode, setExitCode] = useState<number | null>(null);\n const procRef = useRef<any>(null);\n const theme = useTheme();\n\n const addLog = (level: LogLevel, text: string) =>\n setLogs(prev => [...prev.slice(-200), { level, text, ts: Date.now() }]);\n\n // Example: run 'echo hello' — replace with your real command\n const runCommand = () => {\n if (running) return;\n setRunning(true);\n setExitCode(null);\n addLog('info', \\`\\${ICON_RUN} Running command...\\`);\n\n const proc = spawn('echo', ['Hello from CLI wrapper!']);\n procRef.current = proc;\n proc.stdout.on('data', (d: Buffer) => {\n for (const line of d.toString().split('\\\\n').filter(Boolean)) {\n addLog('info', line);\n }\n });\n proc.stderr.on('data', (d: Buffer) => {\n for (const line of d.toString().split('\\\\n').filter(Boolean)) {\n addLog('error', line);\n }\n });\n proc.on('close', (code: number | null) => {\n setRunning(false);\n setExitCode(code);\n addLog(code === 0 ? 'info' : 'error',\n \\`\\${code === 0 ? ICON_DONE : ICON_ERR} Process exited with code \\${code ?? 'null'}\\`);\n procRef.current = null;\n });\n };\n\n // Auto-run on mount, kill process on unmount\n useEffect(() => {\n runCommand();\n return () => {\n if (procRef.current) {\n procRef.current.kill();\n procRef.current = null;\n }\n };\n }, []);\n\n useKeymap([\n { key: 'q', action: () => process.exit(0), description: 'Quit' },\n { key: 'c', ctrl: true, action: () => process.exit(0), description: 'Quit' },\n { key: 'r', action: runCommand, description: 'Re-run command' },\n { key: 'l', action: () => setLogs([]), description: 'Clear logs' },\n ]);\n\n return (\n <box flexDirection=\"column\" padding={1}>\n <row gap={2}>\n <text bold color={theme.colors.primary}>${config.name}</text>\n <text color={running ? theme.colors.warning : theme.colors.muted}>\n {running ? 'Running...' : exitCode === null ? 'Ready' : \\`Exit: \\${exitCode}\\`}\n </text>\n <spacer />\n <text color={theme.colors.muted}>r: re-run | l: clear | q: quit</text>\n </row>\n <text>{SEP}</text>\n\n <box flexDirection=\"column\" flexGrow={1}>\n {logs.map((line, i) => (\n <row key={i} gap={1}>\n <text color={theme.colors.muted} dim>\n {new Date(line.ts).toLocaleTimeString()}\n </text>\n <text color={levelColor(line.level)} bold>\n {line.level.toUpperCase().padEnd(5)}\n </text>\n <text>{line.text}</text>\n </row>\n ))}\n </box>\n\n {!caps.color && (\n <text color=\"yellow\" dim>\n Note: running in a terminal without color support (TERM={process.env.TERM ?? 'unset'})\n </text>\n )}\n </box>\n );\n}\n\nfunction App() {\n return (\n <AutoThemeProvider>\n <ErrorBoundary fallback={(err) => (\n <box border=\"single\" borderColor=\"red\" padding={1}>\n <text color=\"red\" bold>CLI Wrapper Error</text>\n <text>{err.message}</text>\n <text color=\"yellow\">Check that the command exists and is executable.</text>\n </box>\n )}>\n <CliWrapper />\n </ErrorBoundary>\n </AutoThemeProvider>\n );\n}\n\nrender(<App />, { title: '${config.name}' });\n`,\n }];\n}\n\n"],"mappings":"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","names":["resolve"]}
package/package.json CHANGED
@@ -1,6 +1,11 @@
1
1
  {
2
2
  "name": "create-termui-app",
3
- "version": "0.1.2",
3
+ "homepage": "https://www.termui.io/docs/getting-started/installation",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "https://github.com/Karanjot786/TermUI"
7
+ },
8
+ "version": "0.1.4",
4
9
  "description": "Scaffold a new TermUI project with templates, themes, and dev server",
5
10
  "type": "module",
6
11
  "bin": {
@@ -11,7 +16,7 @@
11
16
  "dist"
12
17
  ],
13
18
  "dependencies": {
14
- "@termuijs/tss": "0.1.2"
19
+ "@termuijs/tss": "0.1.4"
15
20
  },
16
21
  "devDependencies": {
17
22
  "tsup": "^8.0.0",