@quincarter/create-lit-app 1.0.15 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,13 +18,17 @@ npx @quincarter/create-lit-app my-app
18
18
 
19
19
  ---
20
20
 
21
- ## Starter Templates
21
+ ## Starter Templates & Interactive TUI
22
22
 
23
- When prompted, you can choose from three starter presets:
23
+ When prompted, an interactive terminal user interface (TUI) lets you easily select options using **Arrow Keys** (or `j`/`k`), **Spacebar** to toggle checkboxes, and **Enter** to confirm.
24
+
25
+ You can choose from three starter presets:
24
26
 
25
27
  1. **Full App Shell Starter**: header, navigation, router, contexts, signals Todo List, cards, charts, and MFE loader.
26
28
  2. **Blank App Shell Host**: minimal Lit App host container with App Shell foundations.
27
- 3. **Custom Selection**: interactively toggle specific architectural features and component showcases.
29
+ 3. **Custom Selection**: interactively toggle specific architectural features in a scrollable TUI checkbox list with automatic dependency checks (e.g. selecting Routing enforces Lit Context).
30
+
31
+ Single-select questions (Package Manager, Git Initialization, Install Dependencies) use interactive TUI radio options.
28
32
 
29
33
  ---
30
34
 
@@ -40,7 +44,7 @@ npx @quincarter/create-lit-app [options]
40
44
  | :--- | :--- | :--- |
41
45
  | `--name=<name>` | Project directory / package name | `my-app-shell` |
42
46
  | `--template=<type>` | Preset: `full` \| `blank` \| `custom` | `full` |
43
- | `--pm=<yarn\|npm>` | Package manager to use | `yarn` |
47
+ | `--pm=<yarn\|npm\|pnpm>` | Package manager to use (`yarn`, `npm`, or `pnpm`) | `yarn` |
44
48
  | `--router` / `--no-router` | `@lit-labs/router` (auto-enables `@lit/context`) | `true` |
45
49
  | `--context` / `--no-context` | `@lit/context` dependency injection | `true` |
46
50
  | `--signals` / `--no-signals` | Preact Signals & IndexedDB store | `true` |
@@ -84,11 +84,20 @@ parse_args() {
84
84
  --no-cards) ENABLE_CARDS=false ;;
85
85
  --charts) ENABLE_CHARTS=true ;;
86
86
  --no-charts) ENABLE_CHARTS=false ;;
87
- --pm=*) PACKAGE_MANAGER="${arg#*=}" ;;
87
+ --pm=*)
88
+ pm_val="${arg#*=}"
89
+ if [[ "$pm_val" =~ ^(yarn|npm|pnpm)$ ]]; then
90
+ PACKAGE_MANAGER="$pm_val"
91
+ else
92
+ log_error "Invalid package manager: $pm_val (must be yarn, npm, or pnpm)"
93
+ exit 1
94
+ fi
95
+ ;;
88
96
  --git) INIT_GIT=true ;;
89
97
  --no-git) INIT_GIT=false ;;
90
98
  --install) RUN_INSTALL=true ;;
91
99
  --no-install) RUN_INSTALL=false ;;
100
+ -y|--yes) NON_INTERACTIVE=true ;;
92
101
  -v|--version)
93
102
  echo "@quincarter/create-lit-app v1.0.12"
94
103
  exit 0
@@ -103,7 +112,7 @@ parse_args() {
103
112
  echo -e "${BOLD}${YELLOW}PROJECT CONFIGURATION:${RESET}"
104
113
  printf " ${CYAN}%-32s${RESET} %s\n" "[app-name], --name=<name>" "Project directory / package name"
105
114
  printf " ${CYAN}%-32s${RESET} %s\n" "--template=<type>" "Starter template: full | blank | custom (default: full)"
106
- printf " ${CYAN}%-32s${RESET} %s\n" "--pm=<yarn|npm>" "Package manager to use (default: yarn)"
115
+ printf " ${CYAN}%-32s${RESET} %s\n" "--pm=<yarn|npm|pnpm>" "Package manager to use (default: yarn)"
107
116
  echo ""
108
117
 
109
118
  echo -e "${BOLD}${YELLOW}CORE ARCHITECTURE & STATE:${RESET}"
@@ -141,6 +150,258 @@ parse_args() {
141
150
  done
142
151
  }
143
152
 
153
+ # Terminal Raw Mode & Input Helpers for TUI
154
+ OLD_STTY=""
155
+
156
+ cleanup_tui() {
157
+ echo -ne "\033[?25h" # Restore cursor
158
+ if [ -n "$OLD_STTY" ]; then
159
+ stty "$OLD_STTY" 2>/dev/null || true
160
+ OLD_STTY=""
161
+ fi
162
+ }
163
+
164
+ trap cleanup_tui EXIT INT TERM
165
+
166
+ read_key() {
167
+ local key subkey
168
+ IFS= read -r -s -n1 key 2>/dev/null || true
169
+ if [[ "$key" == $'\x1b' ]]; then
170
+ read -r -s -n2 -t 0.05 subkey 2>/dev/null || true
171
+ case "$subkey" in
172
+ "[A") echo "UP" ;;
173
+ "[B") echo "DOWN" ;;
174
+ "[C") echo "RIGHT" ;;
175
+ "[D") echo "LEFT" ;;
176
+ *) echo "ESC" ;;
177
+ esac
178
+ elif [[ "$key" == "" ]]; then
179
+ echo "ENTER"
180
+ elif [[ "$key" == " " ]]; then
181
+ echo "SPACE"
182
+ elif [[ "$key" == "k" ]]; then
183
+ echo "UP"
184
+ elif [[ "$key" == "j" ]]; then
185
+ echo "DOWN"
186
+ elif [[ "$key" =~ [1-9] ]]; then
187
+ echo "NUM_$key"
188
+ else
189
+ echo "OTHER"
190
+ fi
191
+ }
192
+
193
+ RADIO_INDEX=0
194
+ RADIO_VALUE=""
195
+
196
+ prompt_radio_menu() {
197
+ local title="$1"
198
+ local default_idx="$2"
199
+ shift 2
200
+ local options=("$@")
201
+ local num_options=${#options[@]}
202
+ local current=$default_idx
203
+
204
+ if [ -n "$TERM" ] && [ -t 0 ]; then
205
+ OLD_STTY=$(stty -g 2>/dev/null)
206
+ stty -echo -icanon min 1 time 0 2>/dev/null || true
207
+ fi
208
+
209
+ echo -ne "\033[?25l" # Hide cursor
210
+
211
+ local rendered=false
212
+ local total_lines=$((num_options + 2))
213
+
214
+ while true; do
215
+ if [ "$rendered" = true ]; then
216
+ echo -ne "\033[${total_lines}A"
217
+ fi
218
+
219
+ echo -e "${BOLD}${CYAN}$title${RESET}\033[K"
220
+ for i in "${!options[@]}"; do
221
+ local opt="${options[$i]}"
222
+ if [ "$i" -eq "$current" ]; then
223
+ echo -e " ${CYAN}❯${RESET} ${BOLD}${GREEN}(*) $opt${RESET}\033[K"
224
+ else
225
+ echo -e " ${BLUE}( )${RESET} $opt\033[K"
226
+ fi
227
+ done
228
+ echo -e "${YELLOW}(Use ↑/↓ or j/k to navigate, Enter to select)${RESET}\033[K"
229
+ rendered=true
230
+
231
+ local key
232
+ key=$(read_key)
233
+ case "$key" in
234
+ UP)
235
+ current=$(( (current - 1 + num_options) % num_options ))
236
+ ;;
237
+ DOWN)
238
+ current=$(( (current + 1) % num_options ))
239
+ ;;
240
+ NUM_*)
241
+ local idx=${key#NUM_}
242
+ idx=$((idx - 1))
243
+ if [ "$idx" -ge 0 ] && [ "$idx" -lt "$num_options" ]; then
244
+ current=$idx
245
+ fi
246
+ ;;
247
+ ENTER|SPACE)
248
+ break
249
+ ;;
250
+ esac
251
+ done
252
+
253
+ if [ "$rendered" = true ]; then
254
+ echo -ne "\033[${total_lines}A"
255
+ for ((l=0; l<total_lines; l++)); do
256
+ echo -ne "\033[2K\r\033[B"
257
+ done
258
+ echo -ne "\033[${total_lines}A"
259
+ fi
260
+
261
+ cleanup_tui
262
+
263
+ RADIO_INDEX=$current
264
+ RADIO_VALUE="${options[$current]}"
265
+ }
266
+
267
+ prompt_checkbox_menu() {
268
+ local title="$1"
269
+ shift 1
270
+ local options=(
271
+ "Routing (@lit-labs/router)"
272
+ "Lit Context (@lit/context)"
273
+ "Preact Signals & IndexedDB Stores (@lit-labs/preact-signals)"
274
+ "Signals Showcase (Todo List Component & Store)"
275
+ "Generic Card Component & Examples Page"
276
+ "Chart.js Wrapper Component & Examples Page"
277
+ "App Shell Header Component"
278
+ "Theme Switcher Component"
279
+ "Micro-Frontend (MFE) Loader Utility"
280
+ )
281
+ local num_options=${#options[@]}
282
+ local current=0
283
+ local status_msg=""
284
+
285
+ # Default state: all features checked
286
+ local checked_states=(1 1 1 1 1 1 1 1 1)
287
+
288
+ enforce_deps() {
289
+ # Rule: If Routing (0) is selected, Context (1) MUST be enabled
290
+ if [ "${checked_states[0]}" -eq 1 ]; then
291
+ checked_states[1]=1
292
+ fi
293
+ # Rule: If Todos Showcase (3) is selected, Signals (2) MUST be enabled
294
+ if [ "${checked_states[3]}" -eq 1 ]; then
295
+ checked_states[2]=1
296
+ fi
297
+ }
298
+
299
+ enforce_deps
300
+
301
+ if [ -n "$TERM" ] && [ -t 0 ]; then
302
+ OLD_STTY=$(stty -g 2>/dev/null)
303
+ stty -echo -icanon min 1 time 0 2>/dev/null || true
304
+ fi
305
+
306
+ echo -ne "\033[?25l" # Hide cursor
307
+
308
+ local rendered=false
309
+ local total_lines=$((num_options + 3))
310
+
311
+ while true; do
312
+ if [ "$rendered" = true ]; then
313
+ echo -ne "\033[${total_lines}A"
314
+ fi
315
+
316
+ echo -e "${BOLD}${CYAN}$title${RESET}\033[K"
317
+ for i in "${!options[@]}"; do
318
+ local opt="${options[$i]}"
319
+ local chk="${checked_states[$i]}"
320
+ local lock_info=""
321
+
322
+ if [ "$i" -eq 1 ] && [ "${checked_states[0]}" -eq 1 ]; then
323
+ lock_info=" ${YELLOW}(required by Routing)${RESET}"
324
+ elif [ "$i" -eq 2 ] && [ "${checked_states[3]}" -eq 1 ]; then
325
+ lock_info=" ${YELLOW}(required by Todos Showcase)${RESET}"
326
+ fi
327
+
328
+ local box_str="[ ]"
329
+ if [ "$chk" -eq 1 ]; then
330
+ box_str="${GREEN}[X]${RESET}"
331
+ else
332
+ box_str="[ ]"
333
+ fi
334
+
335
+ if [ "$i" -eq "$current" ]; then
336
+ echo -e " ${CYAN}❯${RESET} ${BOLD}${box_str} ${opt}${RESET}${lock_info}\033[K"
337
+ else
338
+ echo -e " ${box_str} ${opt}${lock_info}\033[K"
339
+ fi
340
+ done
341
+
342
+ if [ -n "$status_msg" ]; then
343
+ echo -e "${YELLOW}⚠ $status_msg${RESET}\033[K"
344
+ else
345
+ echo -e "\033[K"
346
+ fi
347
+
348
+ echo -e "${YELLOW}(Use ↑/↓ or j/k to navigate, Space to toggle, Enter to confirm)${RESET}\033[K"
349
+ rendered=true
350
+
351
+ status_msg=""
352
+ local key
353
+ key=$(read_key)
354
+ case "$key" in
355
+ UP)
356
+ current=$(( (current - 1 + num_options) % num_options ))
357
+ ;;
358
+ DOWN)
359
+ current=$(( (current + 1) % num_options ))
360
+ ;;
361
+ NUM_*)
362
+ local idx=${key#NUM_}
363
+ idx=$((idx - 1))
364
+ if [ "$idx" -ge 0 ] && [ "$idx" -lt "$num_options" ]; then
365
+ current=$idx
366
+ fi
367
+ ;;
368
+ SPACE)
369
+ if [ "$current" -eq 1 ] && [ "${checked_states[0]}" -eq 1 ]; then
370
+ status_msg="Lit Context is required when Routing is enabled."
371
+ elif [ "$current" -eq 2 ] && [ "${checked_states[3]}" -eq 1 ]; then
372
+ status_msg="Preact Signals is required when Todos Showcase is enabled."
373
+ else
374
+ checked_states[$current]=$(( 1 - checked_states[$current] ))
375
+ enforce_deps
376
+ fi
377
+ ;;
378
+ ENTER)
379
+ break
380
+ ;;
381
+ esac
382
+ done
383
+
384
+ if [ "$rendered" = true ]; then
385
+ echo -ne "\033[${total_lines}A"
386
+ for ((l=0; l<total_lines; l++)); do
387
+ echo -ne "\033[2K\r\033[B"
388
+ done
389
+ echo -ne "\033[${total_lines}A"
390
+ fi
391
+
392
+ cleanup_tui
393
+
394
+ ENABLE_ROUTER=$([ "${checked_states[0]}" -eq 1 ] && echo true || echo false)
395
+ ENABLE_CONTEXT=$([ "${checked_states[1]}" -eq 1 ] && echo true || echo false)
396
+ ENABLE_SIGNALS=$([ "${checked_states[2]}" -eq 1 ] && echo true || echo false)
397
+ ENABLE_TODOS=$([ "${checked_states[3]}" -eq 1 ] && echo true || echo false)
398
+ ENABLE_CARDS=$([ "${checked_states[4]}" -eq 1 ] && echo true || echo false)
399
+ ENABLE_CHARTS=$([ "${checked_states[5]}" -eq 1 ] && echo true || echo false)
400
+ ENABLE_HEADER=$([ "${checked_states[6]}" -eq 1 ] && echo true || echo false)
401
+ ENABLE_THEME_SWITCHER=$([ "${checked_states[7]}" -eq 1 ] && echo true || echo false)
402
+ ENABLE_MFE_LOADER=$([ "${checked_states[8]}" -eq 1 ] && echo true || echo false)
403
+ }
404
+
144
405
  prompt_user() {
145
406
  log_title "Lit Element App Shell Generator"
146
407
 
@@ -151,14 +412,13 @@ prompt_user() {
151
412
  echo -e "Creating app: ${BOLD}${CYAN}$APP_NAME${RESET}\n"
152
413
  fi
153
414
 
154
- echo -e "\nChoose starter template preset:"
155
- echo " 1) Full App Shell (Header, Navigation, Router, Contexts, Signals, Todos Showcase, Cards, Charts, MFEs)"
156
- echo " 2) Blank App Shell (Minimal Lit App Host)"
157
- echo " 3) Custom Selection (Select specific features interactively)"
158
- read -rp "Selection [1-3] (default: 1): " template_choice
415
+ prompt_radio_menu "Choose starter template preset:" 0 \
416
+ "Full App Shell (Header, Navigation, Router, Contexts, Signals, Todos Showcase, Cards, Charts, MFEs)" \
417
+ "Blank App Shell (Minimal Lit App Host)" \
418
+ "Custom Selection (Select specific features interactively)"
159
419
 
160
- case $template_choice in
161
- 2)
420
+ case $RADIO_INDEX in
421
+ 1)
162
422
  TEMPLATE="blank"
163
423
  ENABLE_ROUTER=false
164
424
  ENABLE_CONTEXT=false
@@ -169,48 +429,13 @@ prompt_user() {
169
429
  ENABLE_TODOS=false
170
430
  ENABLE_CARDS=false
171
431
  ENABLE_CHARTS=false
432
+ log_success "Template preset: Blank App Shell"
172
433
  ;;
173
- 3)
434
+ 2)
174
435
  TEMPLATE="custom"
175
- read -rp "Enable Routing (@lit-labs/router)? [Y/n]: " ans
176
- if [[ "$ans" =~ ^[Nn] ]]; then
177
- ENABLE_ROUTER=false
178
- else
179
- ENABLE_ROUTER=true
180
- ENABLE_CONTEXT=true
181
- log_info "Lit Context (@lit/context) automatically enabled for Routing."
182
- fi
183
-
184
- if [ "$ENABLE_ROUTER" = false ]; then
185
- read -rp "Enable Lit Context (@lit/context)? [Y/n]: " ans
186
- [[ "$ans" =~ ^[Nn] ]] && ENABLE_CONTEXT=false || ENABLE_CONTEXT=true
187
- fi
188
-
189
- read -rp "Enable Preact Signals & IndexedDB Stores (@lit-labs/preact-signals)? [Y/n]: " ans
190
- [[ "$ans" =~ ^[Nn] ]] && ENABLE_SIGNALS=false || ENABLE_SIGNALS=true
191
-
192
- read -rp "Include Signals Showcase (Todo List Component & Store)? [Y/n]: " ans
193
- if [[ "$ans" =~ ^[Nn] ]]; then
194
- ENABLE_TODOS=false
195
- else
196
- ENABLE_TODOS=true
197
- ENABLE_SIGNALS=true
198
- fi
199
-
200
- read -rp "Include Generic Card Component & Examples Page? [Y/n]: " ans
201
- [[ "$ans" =~ ^[Nn] ]] && ENABLE_CARDS=false || ENABLE_CARDS=true
202
-
203
- read -rp "Include Chart.js Wrapper Component & Examples Page? [Y/n]: " ans
204
- [[ "$ans" =~ ^[Nn] ]] && ENABLE_CHARTS=false || ENABLE_CHARTS=true
205
-
206
- read -rp "Enable App Shell Header component? [Y/n]: " ans
207
- [[ "$ans" =~ ^[Nn] ]] && ENABLE_HEADER=false || ENABLE_HEADER=true
208
-
209
- read -rp "Enable Theme Switcher component? [Y/n]: " ans
210
- [[ "$ans" =~ ^[Nn] ]] && ENABLE_THEME_SWITCHER=false || ENABLE_THEME_SWITCHER=true
211
-
212
- read -rp "Enable Micro-Frontend (MFE) Loader utility? [Y/n]: " ans
213
- [[ "$ans" =~ ^[Nn] ]] && ENABLE_MFE_LOADER=false || ENABLE_MFE_LOADER=true
436
+ log_success "Template preset: Custom Selection"
437
+ prompt_checkbox_menu "Select features to include in your App Shell:"
438
+ log_success "Custom features configured."
214
439
  ;;
215
440
  *)
216
441
  TEMPLATE="full"
@@ -223,17 +448,31 @@ prompt_user() {
223
448
  ENABLE_TODOS=true
224
449
  ENABLE_CARDS=true
225
450
  ENABLE_CHARTS=true
451
+ log_success "Template preset: Full App Shell"
226
452
  ;;
227
453
  esac
228
454
 
229
- read -rp "Choose package manager [yarn/npm] (default: yarn): " pm_choice
230
- PACKAGE_MANAGER=${pm_choice:-yarn}
455
+ prompt_radio_menu "Choose package manager:" 0 "yarn" "npm" "pnpm"
456
+ PACKAGE_MANAGER="$RADIO_VALUE"
457
+ log_success "Package manager selected: $PACKAGE_MANAGER"
231
458
 
232
- read -rp "Initialize Git repository? [Y/n]: " git_ans
233
- [[ "$git_ans" =~ ^[Nn] ]] && INIT_GIT=false || INIT_GIT=true
459
+ prompt_radio_menu "Initialize Git repository?" 0 "Yes (Initialize Git repository)" "No"
460
+ if [ "$RADIO_INDEX" -eq 0 ]; then
461
+ INIT_GIT=true
462
+ log_success "Git repository: Yes"
463
+ else
464
+ INIT_GIT=false
465
+ log_success "Git repository: No"
466
+ fi
234
467
 
235
- read -rp "Run dependency installation now? [y/N]: " install_ans
236
- [[ "$install_ans" =~ ^[Yy] ]] && RUN_INSTALL=true || RUN_INSTALL=false
468
+ prompt_radio_menu "Run dependency installation now?" 0 "No (Skip dependency installation)" "Yes (Install dependencies now)"
469
+ if [ "$RADIO_INDEX" -eq 1 ]; then
470
+ RUN_INSTALL=true
471
+ log_success "Install dependencies: Yes"
472
+ else
473
+ RUN_INSTALL=false
474
+ log_success "Install dependencies: No"
475
+ fi
237
476
  }
238
477
 
239
478
  parse_args "$@"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quincarter/create-lit-app",
3
- "version": "1.0.15",
3
+ "version": "1.1.0",
4
4
  "description": "Interactive CLI initializer for Lit Element App Shell architecture",
5
5
  "type": "module",
6
6
  "bin": {