@quincarter/create-lit-app 1.0.15 → 1.1.1

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,260 @@ 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
+ stty min 0 time 1 2>/dev/null || true
171
+ read -r -s -n2 subkey 2>/dev/null || true
172
+ stty min 1 time 0 2>/dev/null || true
173
+ case "$subkey" in
174
+ "[A"|"OA") echo "UP" ;;
175
+ "[B"|"OB") echo "DOWN" ;;
176
+ "[C"|"OC") echo "RIGHT" ;;
177
+ "[D"|"OD") echo "LEFT" ;;
178
+ *) echo "ESC" ;;
179
+ esac
180
+ elif [[ "$key" == "" ]]; then
181
+ echo "ENTER"
182
+ elif [[ "$key" == " " ]]; then
183
+ echo "SPACE"
184
+ elif [[ "$key" == "k" ]]; then
185
+ echo "UP"
186
+ elif [[ "$key" == "j" ]]; then
187
+ echo "DOWN"
188
+ elif [[ "$key" =~ [1-9] ]]; then
189
+ echo "NUM_$key"
190
+ else
191
+ echo "OTHER"
192
+ fi
193
+ }
194
+
195
+ RADIO_INDEX=0
196
+ RADIO_VALUE=""
197
+
198
+ prompt_radio_menu() {
199
+ local title="$1"
200
+ local default_idx="$2"
201
+ shift 2
202
+ local options=("$@")
203
+ local num_options=${#options[@]}
204
+ local current=$default_idx
205
+
206
+ if [ -n "$TERM" ] && [ -t 0 ]; then
207
+ OLD_STTY=$(stty -g 2>/dev/null)
208
+ stty -echo -icanon min 1 time 0 2>/dev/null || true
209
+ fi
210
+
211
+ echo -ne "\033[?25l" # Hide cursor
212
+
213
+ local rendered=false
214
+ local total_lines=$((num_options + 2))
215
+
216
+ while true; do
217
+ if [ "$rendered" = true ]; then
218
+ echo -ne "\033[${total_lines}A"
219
+ fi
220
+
221
+ echo -e "${BOLD}${CYAN}$title${RESET}\033[K"
222
+ for i in "${!options[@]}"; do
223
+ local opt="${options[$i]}"
224
+ if [ "$i" -eq "$current" ]; then
225
+ echo -e " ${CYAN}❯${RESET} ${BOLD}${GREEN}(*) $opt${RESET}\033[K"
226
+ else
227
+ echo -e " ${BLUE}( )${RESET} $opt\033[K"
228
+ fi
229
+ done
230
+ echo -e "${YELLOW}(Use ↑/↓ or j/k to navigate, Enter to select)${RESET}\033[K"
231
+ rendered=true
232
+
233
+ local key
234
+ key=$(read_key)
235
+ case "$key" in
236
+ UP)
237
+ current=$(( (current - 1 + num_options) % num_options ))
238
+ ;;
239
+ DOWN)
240
+ current=$(( (current + 1) % num_options ))
241
+ ;;
242
+ NUM_*)
243
+ local idx=${key#NUM_}
244
+ idx=$((idx - 1))
245
+ if [ "$idx" -ge 0 ] && [ "$idx" -lt "$num_options" ]; then
246
+ current=$idx
247
+ fi
248
+ ;;
249
+ ENTER|SPACE)
250
+ break
251
+ ;;
252
+ esac
253
+ done
254
+
255
+ if [ "$rendered" = true ]; then
256
+ echo -ne "\033[${total_lines}A"
257
+ for ((l=0; l<total_lines; l++)); do
258
+ echo -ne "\033[2K\r\033[B"
259
+ done
260
+ echo -ne "\033[${total_lines}A"
261
+ fi
262
+
263
+ cleanup_tui
264
+
265
+ RADIO_INDEX=$current
266
+ RADIO_VALUE="${options[$current]}"
267
+ }
268
+
269
+ prompt_checkbox_menu() {
270
+ local title="$1"
271
+ shift 1
272
+ local options=(
273
+ "Routing (@lit-labs/router)"
274
+ "Lit Context (@lit/context)"
275
+ "Preact Signals & IndexedDB Stores (@lit-labs/preact-signals)"
276
+ "Signals Showcase (Todo List Component & Store)"
277
+ "Generic Card Component & Examples Page"
278
+ "Chart.js Wrapper Component & Examples Page"
279
+ "App Shell Header Component"
280
+ "Theme Switcher Component"
281
+ "Micro-Frontend (MFE) Loader Utility"
282
+ )
283
+ local num_options=${#options[@]}
284
+ local current=0
285
+ local status_msg=""
286
+
287
+ # Default state: all features checked
288
+ local checked_states=(1 1 1 1 1 1 1 1 1)
289
+
290
+ enforce_deps() {
291
+ # Rule: If Routing (0) is selected, Context (1) MUST be enabled
292
+ if [ "${checked_states[0]}" -eq 1 ]; then
293
+ checked_states[1]=1
294
+ fi
295
+ # Rule: If Todos Showcase (3) is selected, Signals (2) MUST be enabled
296
+ if [ "${checked_states[3]}" -eq 1 ]; then
297
+ checked_states[2]=1
298
+ fi
299
+ }
300
+
301
+ enforce_deps
302
+
303
+ if [ -n "$TERM" ] && [ -t 0 ]; then
304
+ OLD_STTY=$(stty -g 2>/dev/null)
305
+ stty -echo -icanon min 1 time 0 2>/dev/null || true
306
+ fi
307
+
308
+ echo -ne "\033[?25l" # Hide cursor
309
+
310
+ local rendered=false
311
+ local total_lines=$((num_options + 3))
312
+
313
+ while true; do
314
+ if [ "$rendered" = true ]; then
315
+ echo -ne "\033[${total_lines}A"
316
+ fi
317
+
318
+ echo -e "${BOLD}${CYAN}$title${RESET}\033[K"
319
+ for i in "${!options[@]}"; do
320
+ local opt="${options[$i]}"
321
+ local chk="${checked_states[$i]}"
322
+ local lock_info=""
323
+
324
+ if [ "$i" -eq 1 ] && [ "${checked_states[0]}" -eq 1 ]; then
325
+ lock_info=" ${YELLOW}(required by Routing)${RESET}"
326
+ elif [ "$i" -eq 2 ] && [ "${checked_states[3]}" -eq 1 ]; then
327
+ lock_info=" ${YELLOW}(required by Todos Showcase)${RESET}"
328
+ fi
329
+
330
+ local box_str="[ ]"
331
+ if [ "$chk" -eq 1 ]; then
332
+ box_str="${GREEN}[X]${RESET}"
333
+ else
334
+ box_str="[ ]"
335
+ fi
336
+
337
+ if [ "$i" -eq "$current" ]; then
338
+ echo -e " ${CYAN}❯${RESET} ${BOLD}${box_str} ${opt}${RESET}${lock_info}\033[K"
339
+ else
340
+ echo -e " ${box_str} ${opt}${lock_info}\033[K"
341
+ fi
342
+ done
343
+
344
+ if [ -n "$status_msg" ]; then
345
+ echo -e "${YELLOW}⚠ $status_msg${RESET}\033[K"
346
+ else
347
+ echo -e "\033[K"
348
+ fi
349
+
350
+ echo -e "${YELLOW}(Use ↑/↓ or j/k to navigate, Space to toggle, Enter to confirm)${RESET}\033[K"
351
+ rendered=true
352
+
353
+ status_msg=""
354
+ local key
355
+ key=$(read_key)
356
+ case "$key" in
357
+ UP)
358
+ current=$(( (current - 1 + num_options) % num_options ))
359
+ ;;
360
+ DOWN)
361
+ current=$(( (current + 1) % num_options ))
362
+ ;;
363
+ NUM_*)
364
+ local idx=${key#NUM_}
365
+ idx=$((idx - 1))
366
+ if [ "$idx" -ge 0 ] && [ "$idx" -lt "$num_options" ]; then
367
+ current=$idx
368
+ fi
369
+ ;;
370
+ SPACE)
371
+ if [ "$current" -eq 1 ] && [ "${checked_states[0]}" -eq 1 ]; then
372
+ status_msg="Lit Context is required when Routing is enabled."
373
+ elif [ "$current" -eq 2 ] && [ "${checked_states[3]}" -eq 1 ]; then
374
+ status_msg="Preact Signals is required when Todos Showcase is enabled."
375
+ else
376
+ checked_states[$current]=$(( 1 - checked_states[$current] ))
377
+ enforce_deps
378
+ fi
379
+ ;;
380
+ ENTER)
381
+ break
382
+ ;;
383
+ esac
384
+ done
385
+
386
+ if [ "$rendered" = true ]; then
387
+ echo -ne "\033[${total_lines}A"
388
+ for ((l=0; l<total_lines; l++)); do
389
+ echo -ne "\033[2K\r\033[B"
390
+ done
391
+ echo -ne "\033[${total_lines}A"
392
+ fi
393
+
394
+ cleanup_tui
395
+
396
+ ENABLE_ROUTER=$([ "${checked_states[0]}" -eq 1 ] && echo true || echo false)
397
+ ENABLE_CONTEXT=$([ "${checked_states[1]}" -eq 1 ] && echo true || echo false)
398
+ ENABLE_SIGNALS=$([ "${checked_states[2]}" -eq 1 ] && echo true || echo false)
399
+ ENABLE_TODOS=$([ "${checked_states[3]}" -eq 1 ] && echo true || echo false)
400
+ ENABLE_CARDS=$([ "${checked_states[4]}" -eq 1 ] && echo true || echo false)
401
+ ENABLE_CHARTS=$([ "${checked_states[5]}" -eq 1 ] && echo true || echo false)
402
+ ENABLE_HEADER=$([ "${checked_states[6]}" -eq 1 ] && echo true || echo false)
403
+ ENABLE_THEME_SWITCHER=$([ "${checked_states[7]}" -eq 1 ] && echo true || echo false)
404
+ ENABLE_MFE_LOADER=$([ "${checked_states[8]}" -eq 1 ] && echo true || echo false)
405
+ }
406
+
144
407
  prompt_user() {
145
408
  log_title "Lit Element App Shell Generator"
146
409
 
@@ -151,14 +414,13 @@ prompt_user() {
151
414
  echo -e "Creating app: ${BOLD}${CYAN}$APP_NAME${RESET}\n"
152
415
  fi
153
416
 
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
417
+ prompt_radio_menu "Choose starter template preset:" 0 \
418
+ "Full App Shell (Header, Navigation, Router, Contexts, Signals, Todos Showcase, Cards, Charts, MFEs)" \
419
+ "Blank App Shell (Minimal Lit App Host)" \
420
+ "Custom Selection (Select specific features interactively)"
159
421
 
160
- case $template_choice in
161
- 2)
422
+ case $RADIO_INDEX in
423
+ 1)
162
424
  TEMPLATE="blank"
163
425
  ENABLE_ROUTER=false
164
426
  ENABLE_CONTEXT=false
@@ -169,48 +431,13 @@ prompt_user() {
169
431
  ENABLE_TODOS=false
170
432
  ENABLE_CARDS=false
171
433
  ENABLE_CHARTS=false
434
+ log_success "Template preset: Blank App Shell"
172
435
  ;;
173
- 3)
436
+ 2)
174
437
  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
438
+ log_success "Template preset: Custom Selection"
439
+ prompt_checkbox_menu "Select features to include in your App Shell:"
440
+ log_success "Custom features configured."
214
441
  ;;
215
442
  *)
216
443
  TEMPLATE="full"
@@ -223,17 +450,31 @@ prompt_user() {
223
450
  ENABLE_TODOS=true
224
451
  ENABLE_CARDS=true
225
452
  ENABLE_CHARTS=true
453
+ log_success "Template preset: Full App Shell"
226
454
  ;;
227
455
  esac
228
456
 
229
- read -rp "Choose package manager [yarn/npm] (default: yarn): " pm_choice
230
- PACKAGE_MANAGER=${pm_choice:-yarn}
457
+ prompt_radio_menu "Choose package manager:" 0 "yarn" "npm" "pnpm"
458
+ PACKAGE_MANAGER="$RADIO_VALUE"
459
+ log_success "Package manager selected: $PACKAGE_MANAGER"
231
460
 
232
- read -rp "Initialize Git repository? [Y/n]: " git_ans
233
- [[ "$git_ans" =~ ^[Nn] ]] && INIT_GIT=false || INIT_GIT=true
461
+ prompt_radio_menu "Initialize Git repository?" 0 "Yes (Initialize Git repository)" "No"
462
+ if [ "$RADIO_INDEX" -eq 0 ]; then
463
+ INIT_GIT=true
464
+ log_success "Git repository: Yes"
465
+ else
466
+ INIT_GIT=false
467
+ log_success "Git repository: No"
468
+ fi
234
469
 
235
- read -rp "Run dependency installation now? [y/N]: " install_ans
236
- [[ "$install_ans" =~ ^[Yy] ]] && RUN_INSTALL=true || RUN_INSTALL=false
470
+ prompt_radio_menu "Run dependency installation now?" 0 "No (Skip dependency installation)" "Yes (Install dependencies now)"
471
+ if [ "$RADIO_INDEX" -eq 1 ]; then
472
+ RUN_INSTALL=true
473
+ log_success "Install dependencies: Yes"
474
+ else
475
+ RUN_INSTALL=false
476
+ log_success "Install dependencies: No"
477
+ fi
237
478
  }
238
479
 
239
480
  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.1",
4
4
  "description": "Interactive CLI initializer for Lit Element App Shell architecture",
5
5
  "type": "module",
6
6
  "bin": {