wad-lab 1.0.1 → 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,73 +28,123 @@ 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 unpack(choice) {
50
- const destDir = process.cwd();
51
-
52
- if (choice === 'all') {
53
- fs.cpSync(templatesRoot, destDir, { recursive: true });
54
- console.log('\nSuccessfully unpacked ALL templates into: ' + destDir);
55
- } else {
56
- const src = path.join(templatesRoot, choice);
57
- const dest = path.join(destDir, choice);
58
- fs.cpSync(src, dest, { recursive: true });
59
- console.log('\nSuccessfully unpacked [' + choice + '] into: ' + dest);
53
+ function promptFolderName(choices) {
54
+ isPrompting = true;
55
+ process.stdin.removeAllListeners('keypress');
56
+ if (process.stdin.isTTY) {
57
+ process.stdin.setRawMode(false);
60
58
  }
61
- process.exit(0);
59
+
60
+ const isAll = choices.includes('all');
61
+ const defaultFolder = isAll || choices.length > 1 ? 'wad-templates' : choices[0];
62
+
63
+ const rl = readline.createInterface({
64
+ input: process.stdin,
65
+ output: process.stdout
66
+ });
67
+
68
+ console.log('\nSelected to unpack: ' + choices.join(', '));
69
+ rl.question(`Enter destination folder name (default: "${defaultFolder}"): `, (answer) => {
70
+ rl.close();
71
+ const folder = answer.trim() || defaultFolder;
72
+ const destDir = path.resolve(process.cwd(), folder);
73
+
74
+ if (!fs.existsSync(destDir)) {
75
+ fs.mkdirSync(destDir, { recursive: true });
76
+ }
77
+
78
+ if (isAll) {
79
+ fs.cpSync(templatesRoot, destDir, { recursive: true });
80
+ console.log('\nSuccessfully unpacked ALL templates into: ' + destDir);
81
+ } else {
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);
89
+ }
90
+ process.exit(0);
91
+ });
62
92
  }
63
93
 
64
94
  if (process.argv[2]) {
65
95
  const arg = process.argv[2];
66
96
  const found = templates.find(t => t.value === arg);
67
97
  if (found) {
68
- unpack(found.value);
98
+ promptFolderName([found.value]);
69
99
  } else {
70
100
  console.error('Unknown template: ' + arg);
71
101
  process.exit(1);
72
102
  }
73
- }
103
+ } else {
104
+ readline.emitKeypressEvents(process.stdin);
105
+ if (process.stdin.isTTY) {
106
+ process.stdin.setRawMode(true);
107
+ }
74
108
 
75
- readline.emitKeypressEvents(process.stdin);
76
- if (process.stdin.isTTY) {
77
- process.stdin.setRawMode(true);
78
- }
109
+ renderMenu();
79
110
 
80
- renderMenu();
111
+ process.stdin.on('keypress', (str, key) => {
112
+ if (isPrompting) return;
81
113
 
82
- process.stdin.on('keypress', (str, key) => {
83
- if (key.ctrl && key.name === 'c') {
84
- process.exit(0);
85
- }
114
+ if (key.ctrl && key.name === 'c') {
115
+ process.exit(0);
116
+ }
86
117
 
87
- if (key.name === 'up') {
88
- selectedIndex = (selectedIndex - 1 + templates.length) % templates.length;
89
- renderMenu();
90
- } else if (key.name === 'down') {
91
- selectedIndex = (selectedIndex + 1) % templates.length;
92
- renderMenu();
93
- } else if (key.name === 'return' || key.name === 'enter') {
94
- if (process.stdin.isTTY) {
95
- process.stdin.setRawMode(false);
118
+ if (key.name === 'up') {
119
+ selectedIndex = (selectedIndex - 1 + templates.length) % templates.length;
120
+ renderMenu();
121
+ } else if (key.name === 'down') {
122
+ selectedIndex = (selectedIndex + 1) % templates.length;
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();
142
+ } else if (key.name === 'return' || key.name === 'enter') {
143
+ let chosen = Array.from(selectedValues);
144
+ if (chosen.length === 0) {
145
+ chosen = [templates[selectedIndex].value];
146
+ }
147
+ promptFolderName(chosen);
96
148
  }
97
- const chosen = templates[selectedIndex].value;
98
- unpack(chosen);
99
- }
100
- });
149
+ });
150
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wad-lab",
3
- "version": "1.0.1",
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
+ }