Skip to content

Commit fb0c14c

Browse files
committed
feat(cli,tui): add package creation wizard (n) and exit code propagation, rearrange install methods order
1 parent 83fb7c5 commit fb0c14c

40 files changed

Lines changed: 1049 additions & 32 deletions

bin/cli.js

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,11 @@ CONTROL PANEL (TUI)
6868
g Settings (GitHub user, SSH, trusted orgs)
6969
r Refresh package list from GitHub (detects new modules)
7070
b Run pnpm run generate
71+
n Create a new app or module (interactive wizard)
7172
q / Esc Quit
7273
${name} init [dir] Create a new OWD project (then opens the control panel)
74+
${name} create Create a new app or module (interactive wizard)
75+
${name} create <kind> <name> Scaffold a new app or module (e.g. desktop create app todo)
7376
${name} add <package> [options]
7477
${name} add <kind> <name> [options]
7578
${name} validate [path...] Check Nuxt module + playground layout
@@ -136,6 +139,7 @@ async function openControlPanel(name) {
136139
const cliDir = join(fileURLToPath(import.meta.url), '..')
137140
const goDir = join(cliDir, '../go')
138141
const binaryPath = join(goDir, 'desktop-go')
142+
const readline = await import('node:readline/promises')
139143

140144
// Compile the Go Control Panel if binary doesn't exist
141145
if (!existsSync(binaryPath)) {
@@ -145,16 +149,52 @@ async function openControlPanel(name) {
145149
console.log('Go Control Panel compiled successfully!')
146150
} catch (err) {
147151
console.warn('Failed to compile Go Control Panel, falling back to legacy CP:', err.message)
148-
const { runCp } = await import('./cp.js')
149-
await runCp(name)
152+
const child = spawn(process.execPath, [
153+
'-e',
154+
`import('./cp.js').then(m => m.runCp(${JSON.stringify(name)}))`
155+
], {
156+
cwd: cliDir,
157+
stdio: 'inherit',
158+
})
159+
child.on('exit', async (code) => {
160+
if (code === 10) {
161+
const { runCreateCli } = await import('./lib/create.js')
162+
const workspaceRoot = findWorkspaceRoot()
163+
try {
164+
await runCreateCli({ workspaceRoot })
165+
} catch (err) {
166+
console.error('\n✗ Error creating package:', err.message)
167+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
168+
await rl.question('\nPress Enter to return to the control panel...')
169+
rl.close()
170+
}
171+
await openControlPanel(name)
172+
} else {
173+
process.exit(code ?? 0)
174+
}
175+
})
150176
return
151177
}
152178
}
153179

154180
// Execute Go TUI
155181
const child = spawn(binaryPath, [], { stdio: 'inherit' })
156-
child.on('exit', (code) => {
157-
process.exit(code ?? 0)
182+
child.on('exit', async (code) => {
183+
if (code === 10) {
184+
const { runCreateCli } = await import('./lib/create.js')
185+
const workspaceRoot = findWorkspaceRoot()
186+
try {
187+
await runCreateCli({ workspaceRoot })
188+
} catch (err) {
189+
console.error('\n✗ Error creating package:', err.message)
190+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
191+
await rl.question('\nPress Enter to return to the control panel...')
192+
rl.close()
193+
}
194+
await openControlPanel(name)
195+
} else {
196+
process.exit(code ?? 0)
197+
}
158198
})
159199
}
160200

@@ -351,6 +391,20 @@ export async function runCli(name, argv, options = {}) {
351391
return
352392
}
353393

394+
if (cmd === 'create' || cmd === 'new') {
395+
if (!workspaceRoot) {
396+
fail(
397+
'Not inside an OWD workspace.',
398+
`Run from inside an OWD workspace directory to scaffold a new package.`,
399+
)
400+
}
401+
const { runCreateCli } = await import('./lib/create.js')
402+
const kind = _[1]
403+
const name = _[2]
404+
await runCreateCli({ kind, name, workspaceRoot })
405+
return
406+
}
407+
354408
if (!cmd) {
355409
if (!workspaceRoot) {
356410
fail(

bin/cp.js

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,7 @@ export async function runCp(commandName = 'desktop') {
489489
{ id: 'docs', label: 'Open documentation', key: 'i' },
490490
{ id: 'settings', label: 'Settings', key: 'g' },
491491
{ id: 'build', label: 'Build (pnpm generate)', key: 'b' },
492+
{ id: 'create', label: 'Create new app or module', key: 'n' },
492493
{ id: 'quit', label: 'Quit control panel', key: 'q' },
493494
]
494495

@@ -1389,7 +1390,7 @@ export async function runCp(commandName = 'desktop') {
13891390
[
13901391
msgLine,
13911392
` Legend: {${C.add}-fg}[+]{/} add {${C.remove}-fg}[-]{/} remove {${C.accent}-fg}[*]{/} on desktop | {${C.npm}-fg}NPM{/} registry {${C.git}-fg}GIT{/} repo {${C.local}-fg}LOC{/} workspace | {${C.warn}-fg}WRN{/} untrusted`,
1392-
` Shortcuts: ↑↓ Space toggle ${keyHint('1')}${keyHint('2')}${keyHint('3')} tabs ${saveShortcut} ${keyHint('d')} server ${keyHint('O')} sort ${keyHint('m')} menu ${keyHint('u')} updates ${keyHint('r')} refresh ${keyHint('q')} quit`,
1393+
` Shortcuts: ↑↓ Space toggle ${keyHint('1')}${keyHint('2')}${keyHint('3')} tabs ${saveShortcut} ${keyHint('d')} server ${keyHint('O')} sort ${keyHint('m')} menu ${keyHint('u')} updates ${keyHint('r')} refresh ${keyHint('n')} new ${keyHint('q')} quit`,
13931394
].join('\n'),
13941395
)
13951396
}
@@ -2037,6 +2038,9 @@ export async function runCp(commandName = 'desktop') {
20372038
refreshTerminalUi()
20382039
}
20392040
break
2041+
case 'create':
2042+
process.exit(10)
2043+
break
20402044
case 'quit':
20412045
process.exit(0)
20422046
break
@@ -2724,30 +2728,35 @@ export async function runCp(commandName = 'desktop') {
27242728
installWizardList.setItems(
27252729
installWizardOptions.map((opt) => {
27262730
const prefix =
2727-
opt.kind === 'npm' ? `{${C.npm}-fg}NPM{/}` : `{${C.git}-fg}GIT{/}`
2731+
opt.kind === 'npm' ? `{${C.npm}-fg}NPM{/}` : opt.kind === 'local' ? `{${C.local}-fg}LOC{/}` : `{${C.git}-fg}GIT{/}`
27282732
return `${prefix} ${opt.label}`
27292733
}),
27302734
)
27312735
let defaultIdx = 0
2732-
const lastId = settings.lastInstallChoiceId
2733-
if (lastId) {
2734-
let found = installWizardOptions.findIndex((opt) => opt.id === lastId)
2735-
if (found >= 0) {
2736-
defaultIdx = found
2737-
} else {
2738-
const isSsh = lastId.includes('ssh')
2739-
const isHttps = lastId.includes('https')
2740-
const isNpm = lastId === 'npm'
2741-
if (isNpm) {
2742-
found = installWizardOptions.findIndex((opt) => opt.id === 'npm')
2743-
if (found >= 0) defaultIdx = found
2736+
const isLocalAvailable = installWizardOptions.some((opt) => opt.id === 'local')
2737+
if (isLocalAvailable) {
2738+
defaultIdx = installWizardOptions.findIndex((opt) => opt.id === 'local')
2739+
} else {
2740+
const lastId = settings.lastInstallChoiceId
2741+
if (lastId) {
2742+
let found = installWizardOptions.findIndex((opt) => opt.id === lastId)
2743+
if (found >= 0) {
2744+
defaultIdx = found
27442745
} else {
2745-
found = installWizardOptions.findIndex((opt) => {
2746-
if (isSsh && opt.protocol === 'ssh') return true
2747-
if (isHttps && opt.protocol === 'https') return true
2748-
return false
2749-
})
2750-
if (found >= 0) defaultIdx = found
2746+
const isSsh = lastId.includes('ssh')
2747+
const isHttps = lastId.includes('https')
2748+
const isNpm = lastId === 'npm'
2749+
if (isNpm) {
2750+
found = installWizardOptions.findIndex((opt) => opt.id === 'npm')
2751+
if (found >= 0) defaultIdx = found
2752+
} else {
2753+
found = installWizardOptions.findIndex((opt) => {
2754+
if (isSsh && opt.protocol === 'ssh') return true
2755+
if (isHttps && opt.protocol === 'https') return true
2756+
return false
2757+
})
2758+
if (found >= 0) defaultIdx = found
2759+
}
27512760
}
27522761
}
27532762
}
@@ -3217,6 +3226,10 @@ export async function runCp(commandName = 'desktop') {
32173226
if (menuOpen) closeMenu()
32183227
else openMenu()
32193228
})
3229+
screen.key(['n', 'N'], () => {
3230+
if (settingsOpen || installWizardOpen || sortPickerOpen || menuOpen || confirmDialogOpen) return
3231+
process.exit(10)
3232+
})
32203233
screen.key(['d', 'D'], () => {
32213234
if (!overlayBlocksKeys()) {
32223235
if (isDevServerUp()) stopDevServer()

0 commit comments

Comments
 (0)