wad-lab 1.0.2 → 1.0.3

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/bin/index.js CHANGED
@@ -28,52 +28,64 @@ const templates = [
28
28
  ];
29
29
 
30
30
  let selectedIndex = 0;
31
+ const selectedValues = new Set();
31
32
  const templatesRoot = path.join(__dirname, '..', 'templates');
33
+ let isPrompting = false;
32
34
 
33
35
  function renderMenu() {
34
36
  console.clear();
35
37
  console.log('=== WAD LAB STARTER TEMPLATES ===');
36
- console.log('Use [UP / DOWN] arrow keys to navigate, press [ENTER] to select:\n');
38
+ console.log('Use [UP / DOWN] arrow keys to move');
39
+ console.log('Press [SPACE] to select / deselect (multi-select supported)');
40
+ console.log('Press [ENTER] to confirm and proceed\n');
37
41
 
38
42
  templates.forEach((t, i) => {
39
- if (i === selectedIndex) {
40
- console.log('> [*] ' + t.name);
41
- } else {
42
- console.log(' [ ] ' + t.name);
43
- }
43
+ const isCursor = i === selectedIndex;
44
+ const isChecked = selectedValues.has(t.value);
45
+ const box = isChecked ? '[x]' : '[ ]';
46
+ const prefix = isCursor ? '> ' : ' ';
47
+ console.log(prefix + box + ' ' + t.name);
44
48
  });
45
49
 
46
50
  console.log('\nPress Ctrl+C to cancel.');
47
51
  }
48
52
 
49
- function promptFolderName(choice) {
53
+ function promptFolderName(choices) {
54
+ isPrompting = true;
55
+ process.stdin.removeAllListeners('keypress');
50
56
  if (process.stdin.isTTY) {
51
57
  process.stdin.setRawMode(false);
52
58
  }
53
59
 
54
- const defaultFolder = choice === 'all' ? 'wad-all-templates' : choice;
60
+ const isAll = choices.includes('all');
61
+ const defaultFolder = isAll || choices.length > 1 ? 'wad-templates' : choices[0];
62
+
55
63
  const rl = readline.createInterface({
56
64
  input: process.stdin,
57
65
  output: process.stdout
58
66
  });
59
67
 
60
- console.log('\nSelected: ' + choice);
68
+ console.log('\nSelected to unpack: ' + choices.join(', '));
61
69
  rl.question(`Enter destination folder name (default: "${defaultFolder}"): `, (answer) => {
62
70
  rl.close();
63
71
  const folder = answer.trim() || defaultFolder;
64
- const dest = path.resolve(process.cwd(), folder);
72
+ const destDir = path.resolve(process.cwd(), folder);
65
73
 
66
- if (!fs.existsSync(dest)) {
67
- fs.mkdirSync(dest, { recursive: true });
74
+ if (!fs.existsSync(destDir)) {
75
+ fs.mkdirSync(destDir, { recursive: true });
68
76
  }
69
77
 
70
- if (choice === 'all') {
71
- fs.cpSync(templatesRoot, dest, { recursive: true });
72
- console.log('\nSuccessfully unpacked ALL templates into: ' + dest);
78
+ if (isAll) {
79
+ fs.cpSync(templatesRoot, destDir, { recursive: true });
80
+ console.log('\nSuccessfully unpacked ALL templates into: ' + destDir);
73
81
  } else {
74
- const src = path.join(templatesRoot, choice);
75
- fs.cpSync(src, dest, { recursive: true });
76
- console.log('\nSuccessfully unpacked [' + choice + '] into: ' + dest);
82
+ choices.forEach(c => {
83
+ const src = path.join(templatesRoot, c);
84
+ const dest = path.join(destDir, c);
85
+ fs.cpSync(src, dest, { recursive: true });
86
+ console.log('Unpacked [' + c + '] into: ' + dest);
87
+ });
88
+ console.log('\nSuccessfully unpacked all chosen templates into: ' + destDir);
77
89
  }
78
90
  process.exit(0);
79
91
  });
@@ -83,7 +95,7 @@ if (process.argv[2]) {
83
95
  const arg = process.argv[2];
84
96
  const found = templates.find(t => t.value === arg);
85
97
  if (found) {
86
- promptFolderName(found.value);
98
+ promptFolderName([found.value]);
87
99
  } else {
88
100
  console.error('Unknown template: ' + arg);
89
101
  process.exit(1);
@@ -97,6 +109,8 @@ if (process.argv[2]) {
97
109
  renderMenu();
98
110
 
99
111
  process.stdin.on('keypress', (str, key) => {
112
+ if (isPrompting) return;
113
+
100
114
  if (key.ctrl && key.name === 'c') {
101
115
  process.exit(0);
102
116
  }
@@ -107,8 +121,29 @@ if (process.argv[2]) {
107
121
  } else if (key.name === 'down') {
108
122
  selectedIndex = (selectedIndex + 1) % templates.length;
109
123
  renderMenu();
124
+ } else if (key.name === 'space') {
125
+ const currentVal = templates[selectedIndex].value;
126
+ if (currentVal === 'all') {
127
+ if (selectedValues.has('all')) {
128
+ selectedValues.clear();
129
+ } else {
130
+ selectedValues.clear();
131
+ selectedValues.add('all');
132
+ }
133
+ } else {
134
+ selectedValues.delete('all');
135
+ if (selectedValues.has(currentVal)) {
136
+ selectedValues.delete(currentVal);
137
+ } else {
138
+ selectedValues.add(currentVal);
139
+ }
140
+ }
141
+ renderMenu();
110
142
  } else if (key.name === 'return' || key.name === 'enter') {
111
- const chosen = templates[selectedIndex].value;
143
+ let chosen = Array.from(selectedValues);
144
+ if (chosen.length === 0) {
145
+ chosen = [templates[selectedIndex].value];
146
+ }
112
147
  promptFolderName(chosen);
113
148
  }
114
149
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wad-lab",
3
- "version": "1.0.2",
4
- "description": "Interactive CLI for WAD Lab starter templates (Spring Boot, MongoDB, Vue 3, Servlets)",
3
+ "version": "1.0.3",
4
+ "description": "Interactive CLI for WAD Lab starter templates with multi-select support",
5
5
  "bin": {
6
6
  "wad-lab": "./bin/index.js"
7
7
  },
@@ -2,26 +2,7 @@
2
2
  <html>
3
3
  <head>
4
4
  <title>Form Validation Demo</title>
5
- <style>
6
- body {
7
- font-family: sans-serif;
8
- margin: 20px;
9
- }
10
- input:focus {
11
- outline: 2px solid black;
12
- }
13
- input:invalid {
14
- border: 1px solid gray;
15
- }
16
- p::before {
17
- content: "• ";
18
- }
19
- table, th, td {
20
- border: 1px solid black;
21
- border-collapse: collapse;
22
- padding: 4px 8px;
23
- }
24
- </style>
5
+ <link rel="stylesheet" href="style.css">
25
6
  </head>
26
7
  <body>
27
8
  <h2>Item Entry Form</h2>
@@ -56,45 +37,6 @@
56
37
  <tbody id="tableBody"></tbody>
57
38
  </table>
58
39
 
59
- <script>
60
- function validateAndSubmit(e) {
61
- e.preventDefault();
62
-
63
- const name = document.getElementById("name").value.trim();
64
- const description = document.getElementById("description").value.trim();
65
- const price = document.getElementById("price").value.trim();
66
- const errorMsg = document.getElementById("errorMsg");
67
-
68
- errorMsg.innerText = "";
69
-
70
- if (name.length < 2) {
71
- errorMsg.innerText = "Name must be at least 2 characters.";
72
- return false;
73
- }
74
-
75
- const priceNum = parseFloat(price);
76
- if (isNaN(priceNum) || priceNum <= 0) {
77
- errorMsg.innerText = "Price must be a positive number.";
78
- return false;
79
- }
80
-
81
- const tbody = document.getElementById("tableBody");
82
- const row = document.createElement("tr");
83
-
84
- row.innerHTML = "<td>" + name + "</td>" +
85
- "<td>" + description + "</td>" +
86
- "<td>" + priceNum.toFixed(2) + "</td>" +
87
- "<td><button onclick='deleteRow(this)'>Delete</button></td>";
88
-
89
- tbody.appendChild(row);
90
-
91
- document.getElementById("itemForm").reset();
92
- return true;
93
- }
94
-
95
- function deleteRow(btn) {
96
- btn.parentElement.parentElement.remove();
97
- }
98
- </script>
40
+ <script src="script.js"></script>
99
41
  </body>
100
42
  </html>
@@ -0,0 +1,38 @@
1
+ function validateAndSubmit(e) {
2
+ e.preventDefault();
3
+
4
+ const name = document.getElementById("name").value.trim();
5
+ const description = document.getElementById("description").value.trim();
6
+ const price = document.getElementById("price").value.trim();
7
+ const errorMsg = document.getElementById("errorMsg");
8
+
9
+ errorMsg.innerText = "";
10
+
11
+ if (name.length < 2) {
12
+ errorMsg.innerText = "Name must be at least 2 characters.";
13
+ return false;
14
+ }
15
+
16
+ const priceNum = parseFloat(price);
17
+ if (isNaN(priceNum) || priceNum <= 0) {
18
+ errorMsg.innerText = "Price must be a positive number.";
19
+ return false;
20
+ }
21
+
22
+ const tbody = document.getElementById("tableBody");
23
+ const row = document.createElement("tr");
24
+
25
+ row.innerHTML = "<td>" + name + "</td>" +
26
+ "<td>" + description + "</td>" +
27
+ "<td>" + priceNum.toFixed(2) + "</td>" +
28
+ "<td><button onclick='deleteRow(this)'>Delete</button></td>";
29
+
30
+ tbody.appendChild(row);
31
+
32
+ document.getElementById("itemForm").reset();
33
+ return true;
34
+ }
35
+
36
+ function deleteRow(btn) {
37
+ btn.parentElement.parentElement.remove();
38
+ }
@@ -0,0 +1,22 @@
1
+ body {
2
+ font-family: sans-serif;
3
+ margin: 20px;
4
+ }
5
+
6
+ input:focus {
7
+ outline: 2px solid black;
8
+ }
9
+
10
+ input:invalid {
11
+ border: 1px solid gray;
12
+ }
13
+
14
+ p::before {
15
+ content: "• ";
16
+ }
17
+
18
+ table, th, td {
19
+ border: 1px solid black;
20
+ border-collapse: collapse;
21
+ padding: 4px 8px;
22
+ }