inquirerjs-checkbox-search 0.5.1 → 1.0.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
@@ -90,7 +90,7 @@ type PageSizeConfig = {
90
90
  1. Start with base page size (from `base` or auto-calculated)
91
91
  2. Calculate buffer:
92
92
  - If `autoBufferDescriptions` is true: Add lines needed for largest description
93
- - Otherwise: Add `buffer` value (if specified)
93
+ - Add `buffer` value (if specified)
94
94
  - Ensure buffer is at least `minBuffer` (if specified)
95
95
  3. Subtract buffer from base page size
96
96
  4. Apply `min`/`max` constraints
@@ -101,21 +101,22 @@ type PageSizeConfig = {
101
101
  ```typescript
102
102
  type CheckboxSearchTheme = {
103
103
  icon: {
104
- checked: string | ((text: string) => string);
105
- unchecked: string | ((text: string) => string);
106
- cursor: string | ((text: string) => string);
104
+ checked: string | ((text: string) => string); // Icon for checked items
105
+ unchecked: string | ((text: string) => string); // Icon for unchecked items
106
+ cursor: string | ((text: string) => string); // Icon for cursor/current item
107
+ nocursor?: string; // Icon/space placeholder for cursor
107
108
  };
108
109
  style: {
109
- answer: (text: string) => string;
110
- message: (text: string) => string;
111
- error: (text: string) => string;
112
- help: (text: string) => string;
113
- highlight: (text: string) => string;
114
- searchTerm: (text: string) => string;
115
- description: (text: string) => string;
116
- disabled: (text: string) => string;
110
+ message: (text: string) => string; // Style for prompt message
111
+ error: (text: string) => string; // Style for error messages
112
+ help: (text: string) => string; // Style for help text
113
+ highlight: (text: string) => string; // Style for highlighted items (takes precedence)
114
+ searchTerm: (text: string) => string; // Style for search term display
115
+ description: (text: string) => string; // Style for item descriptions
116
+ disabled: (text: string) => string; // Style for disabled items
117
+ checked: (text: string) => string; // Style for checked items
117
118
  };
118
- helpMode: 'always' | 'never' | 'auto';
119
+ helpMode: 'always' | 'never' | 'auto'; // When to show help text
119
120
  };
120
121
  ```
121
122
 
@@ -220,20 +221,17 @@ npm run prepublishOnly # Full build + test + validation pipeline
220
221
  #### File Structure
221
222
 
222
223
  ```
223
- ├── src/
224
- │ ├── index.ts # Main prompt implementation
225
- │ └── *.test.ts # Test files (excluded from build)
226
- ├── dist/ # Built output (generated by tshy)
227
- │ ├── esm/ # ES modules
228
- │ └── commonjs/ # CommonJS
229
- ├── .github/
230
- │ └── workflows/ # CI/CD workflows
231
- ├── node_modules/ # Dependencies
232
- ├── package.json # Package config with tshy setup
233
- ├── tsconfig.json # TypeScript config
234
- ├── vitest.config.ts # Test config
235
- ├── eslint.config.js # Linting config
236
- └── .prettierrc # Formatting config
224
+ ├── src/ # actual source code
225
+ │ ├── index.ts
226
+ │ └── __tests__/
227
+ ├── dist/ # build output
228
+ │ ├── esm/
229
+ │ └── commonjs/
230
+ ├── examples/ # usage examples
231
+ ├── demos/ # scripts to record GIFs from examples
232
+ ├── docs/ # documentation resources
233
+ │ └── img/
234
+ └── scripts/ # build and utility scripts
237
235
  ```
238
236
 
239
237
  #### Demo Generation
@@ -247,12 +245,12 @@ To generate demos locally, run `npm run demo:generate:all` (requires `docker`).
247
245
  1. **Before Committing**
248
246
 
249
247
  ```bash
250
- npm test # Run all quality checks + tests
248
+ npm test
251
249
  ```
252
250
 
253
251
  2. **Before Publishing**
254
252
  ```bash
255
- npm run prepublishOnly # Full validation pipeline
253
+ npm run prepublishOnly
256
254
  ```
257
255
 
258
256
  #### Build & Release Workflow
@@ -260,19 +258,20 @@ To generate demos locally, run `npm run demo:generate:all` (requires `docker`).
260
258
  1. **Development Build**
261
259
 
262
260
  ```bash
263
- npm run build # One-time build
264
- npm run dev # Watch mode for development
261
+ npm run build
262
+ npm run dev
265
263
  ```
266
264
 
267
265
  2. **Package Validation**
268
266
 
269
267
  ```bash
270
- npm run attw # Validate dual exports work correctly
268
+ npm run attw
271
269
  ```
272
270
 
273
271
  3. **Release Process** (Automated)
274
272
  - Push changes to `main` branch
275
273
  - Release Please creates/updates release PR
274
+ - demo images are generated and added to the PR
276
275
  - Merge release PR to trigger:
277
276
  - GitHub release creation
278
277
  - npm package publication
@@ -8,9 +8,9 @@ type CheckboxSearchTheme = {
8
8
  checked: string | ((text: string) => string);
9
9
  unchecked: string | ((text: string) => string);
10
10
  cursor: string | ((text: string) => string);
11
+ nocursor?: string | ((text: string) => string);
11
12
  };
12
13
  style: {
13
- answer: (text: string) => string;
14
14
  message: (text: string) => string;
15
15
  error: (text: string) => string;
16
16
  help: (text: string) => string;
@@ -18,6 +18,7 @@ type CheckboxSearchTheme = {
18
18
  searchTerm: (text: string) => string;
19
19
  description: (text: string) => string;
20
20
  disabled: (text: string) => string;
21
+ checked: (text: string) => string;
21
22
  };
22
23
  helpMode: 'always' | 'never' | 'auto';
23
24
  };
@@ -20,9 +20,9 @@ const checkboxSearchTheme = {
20
20
  checked: yoctocolors_cjs_1.default.green(figures_1.default.circleFilled),
21
21
  unchecked: figures_1.default.circle,
22
22
  cursor: figures_1.default.pointer,
23
+ nocursor: ' ', // Default: single space when cursor is not active
23
24
  },
24
25
  style: {
25
- answer: yoctocolors_cjs_1.default.cyan,
26
26
  message: yoctocolors_cjs_1.default.cyan,
27
27
  error: (text) => yoctocolors_cjs_1.default.yellow(`> ${text}`),
28
28
  help: yoctocolors_cjs_1.default.dim,
@@ -30,6 +30,7 @@ const checkboxSearchTheme = {
30
30
  searchTerm: yoctocolors_cjs_1.default.cyan,
31
31
  description: yoctocolors_cjs_1.default.cyan,
32
32
  disabled: yoctocolors_cjs_1.default.dim,
33
+ checked: yoctocolors_cjs_1.default.green,
33
34
  },
34
35
  helpMode: 'always',
35
36
  };
@@ -190,10 +191,8 @@ function resolvePageSize(pageSize, items) {
190
191
  if (pageSize.autoBufferDescriptions) {
191
192
  buffer += calculateDescriptionLines(items, pageSize.autoBufferCountsLineWidth || false);
192
193
  }
193
- else {
194
- // 2c: Add buffer value (only if not auto-buffering)
195
- buffer += pageSize.buffer || 0;
196
- }
194
+ // 2c: Add buffer value (always if specified)
195
+ buffer += pageSize.buffer || 0;
197
196
  // 2d: Ensure at least minBuffer
198
197
  if (pageSize.minBuffer !== undefined) {
199
198
  buffer = Math.max(buffer, pageSize.minBuffer);
@@ -537,26 +536,27 @@ exports.default = (0, core_1.createPrompt)((config, done) => {
537
536
  }
538
537
  return undefined;
539
538
  }, [active, filteredItems]);
540
- // Create renderItem function that's reactive to current state
539
+ // Helper function to resolve icon (string or function)
540
+ const resolveIcon = (icon, choiceText) => {
541
+ return typeof icon === 'function' ? icon(choiceText) : icon;
542
+ };
543
+ // Create renderItem function that's reactive to current state but minimizes recreations
541
544
  const renderItem = (0, core_1.useMemo)(() => {
542
545
  return ({ item, isActive }) => {
543
546
  const line = [];
544
547
  if (core_1.Separator.isSeparator(item)) {
545
548
  return yoctocolors_cjs_1.default.dim(item.separator);
546
549
  }
547
- // Look up checked state directly from allItems to get the current state
548
- const currentItem = allItems.find((allItem) => !core_1.Separator.isSeparator(allItem) &&
549
- allItem.value === item.value);
550
- const isChecked = currentItem?.checked || false;
551
- // Helper function to resolve icon (string or function)
552
- const resolveIcon = (icon, choiceText) => {
553
- return typeof icon === 'function' ? icon(choiceText) : icon;
554
- };
550
+ // Direct access to the checked state (item is from the same source as allItems)
551
+ const isChecked = !core_1.Separator.isSeparator(item) &&
552
+ item.checked;
555
553
  const choiceName = item.name;
556
554
  const checkbox = resolveIcon(isChecked ? theme.icon.checked : theme.icon.unchecked, choiceName);
555
+ // If active, use the cursor icon, otherwise use appropriate spacing to align items
557
556
  const cursor = isActive
558
557
  ? resolveIcon(theme.icon.cursor, choiceName)
559
- : ' ';
558
+ : resolveIcon(theme.icon.nocursor ?? ' ', choiceName);
559
+ // Keep cursor and checkbox as separate elements so join(' ') adds the proper space between them
560
560
  line.push(cursor, checkbox);
561
561
  let text = item.name;
562
562
  if (isActive) {
@@ -566,6 +566,9 @@ exports.default = (0, core_1.createPrompt)((config, done) => {
566
566
  else if (item.disabled) {
567
567
  text = theme.style.disabled(text);
568
568
  }
569
+ else if (isChecked) {
570
+ text = theme.style.checked(text);
571
+ }
569
572
  line.push(text);
570
573
  // Show disabled reason if item is disabled (but no descriptions inline anymore)
571
574
  if (item.disabled) {
@@ -577,7 +580,7 @@ exports.default = (0, core_1.createPrompt)((config, done) => {
577
580
  // NOTE: Removed the inline description display - descriptions now appear at bottom
578
581
  return line.join(' ');
579
582
  };
580
- }, [allItems, theme, config.theme]);
583
+ }, [theme, config.theme]);
581
584
  // Setup pagination
582
585
  const page = (0, core_1.usePagination)({
583
586
  items: filteredItems,
@@ -8,9 +8,9 @@ type CheckboxSearchTheme = {
8
8
  checked: string | ((text: string) => string);
9
9
  unchecked: string | ((text: string) => string);
10
10
  cursor: string | ((text: string) => string);
11
+ nocursor?: string | ((text: string) => string);
11
12
  };
12
13
  style: {
13
- answer: (text: string) => string;
14
14
  message: (text: string) => string;
15
15
  error: (text: string) => string;
16
16
  help: (text: string) => string;
@@ -18,6 +18,7 @@ type CheckboxSearchTheme = {
18
18
  searchTerm: (text: string) => string;
19
19
  description: (text: string) => string;
20
20
  disabled: (text: string) => string;
21
+ checked: (text: string) => string;
21
22
  };
22
23
  helpMode: 'always' | 'never' | 'auto';
23
24
  };
package/dist/esm/index.js CHANGED
@@ -10,9 +10,9 @@ const checkboxSearchTheme = {
10
10
  checked: colors.green(figures.circleFilled),
11
11
  unchecked: figures.circle,
12
12
  cursor: figures.pointer,
13
+ nocursor: ' ', // Default: single space when cursor is not active
13
14
  },
14
15
  style: {
15
- answer: colors.cyan,
16
16
  message: colors.cyan,
17
17
  error: (text) => colors.yellow(`> ${text}`),
18
18
  help: colors.dim,
@@ -20,6 +20,7 @@ const checkboxSearchTheme = {
20
20
  searchTerm: colors.cyan,
21
21
  description: colors.cyan,
22
22
  disabled: colors.dim,
23
+ checked: colors.green,
23
24
  },
24
25
  helpMode: 'always',
25
26
  };
@@ -180,10 +181,8 @@ export function resolvePageSize(pageSize, items) {
180
181
  if (pageSize.autoBufferDescriptions) {
181
182
  buffer += calculateDescriptionLines(items, pageSize.autoBufferCountsLineWidth || false);
182
183
  }
183
- else {
184
- // 2c: Add buffer value (only if not auto-buffering)
185
- buffer += pageSize.buffer || 0;
186
- }
184
+ // 2c: Add buffer value (always if specified)
185
+ buffer += pageSize.buffer || 0;
187
186
  // 2d: Ensure at least minBuffer
188
187
  if (pageSize.minBuffer !== undefined) {
189
188
  buffer = Math.max(buffer, pageSize.minBuffer);
@@ -527,26 +526,27 @@ export default createPrompt((config, done) => {
527
526
  }
528
527
  return undefined;
529
528
  }, [active, filteredItems]);
530
- // Create renderItem function that's reactive to current state
529
+ // Helper function to resolve icon (string or function)
530
+ const resolveIcon = (icon, choiceText) => {
531
+ return typeof icon === 'function' ? icon(choiceText) : icon;
532
+ };
533
+ // Create renderItem function that's reactive to current state but minimizes recreations
531
534
  const renderItem = useMemo(() => {
532
535
  return ({ item, isActive }) => {
533
536
  const line = [];
534
537
  if (Separator.isSeparator(item)) {
535
538
  return colors.dim(item.separator);
536
539
  }
537
- // Look up checked state directly from allItems to get the current state
538
- const currentItem = allItems.find((allItem) => !Separator.isSeparator(allItem) &&
539
- allItem.value === item.value);
540
- const isChecked = currentItem?.checked || false;
541
- // Helper function to resolve icon (string or function)
542
- const resolveIcon = (icon, choiceText) => {
543
- return typeof icon === 'function' ? icon(choiceText) : icon;
544
- };
540
+ // Direct access to the checked state (item is from the same source as allItems)
541
+ const isChecked = !Separator.isSeparator(item) &&
542
+ item.checked;
545
543
  const choiceName = item.name;
546
544
  const checkbox = resolveIcon(isChecked ? theme.icon.checked : theme.icon.unchecked, choiceName);
545
+ // If active, use the cursor icon, otherwise use appropriate spacing to align items
547
546
  const cursor = isActive
548
547
  ? resolveIcon(theme.icon.cursor, choiceName)
549
- : ' ';
548
+ : resolveIcon(theme.icon.nocursor ?? ' ', choiceName);
549
+ // Keep cursor and checkbox as separate elements so join(' ') adds the proper space between them
550
550
  line.push(cursor, checkbox);
551
551
  let text = item.name;
552
552
  if (isActive) {
@@ -556,6 +556,9 @@ export default createPrompt((config, done) => {
556
556
  else if (item.disabled) {
557
557
  text = theme.style.disabled(text);
558
558
  }
559
+ else if (isChecked) {
560
+ text = theme.style.checked(text);
561
+ }
559
562
  line.push(text);
560
563
  // Show disabled reason if item is disabled (but no descriptions inline anymore)
561
564
  if (item.disabled) {
@@ -567,7 +570,7 @@ export default createPrompt((config, done) => {
567
570
  // NOTE: Removed the inline description display - descriptions now appear at bottom
568
571
  return line.join(' ');
569
572
  };
570
- }, [allItems, theme, config.theme]);
573
+ }, [theme, config.theme]);
571
574
  // Setup pagination
572
575
  const page = usePagination({
573
576
  items: filteredItems,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inquirerjs-checkbox-search",
3
- "version": "0.5.1",
3
+ "version": "1.0.0",
4
4
  "description": "A multi-select prompt with text filtering for inquirer.js",
5
5
  "keywords": [
6
6
  "answer",
@@ -86,6 +86,7 @@
86
86
  "demo:generate": "node scripts/generate-demo.js",
87
87
  "demo:generate:basic": "node scripts/generate-demo.js basic",
88
88
  "demo:generate:validation": "node scripts/generate-demo.js validation",
89
+ "demo:generate:custom-theme": "node scripts/generate-demo.js custom-theme",
89
90
  "demo:generate:all": "node scripts/generate-demo.js all"
90
91
  },
91
92
  "dependencies": {
@@ -112,7 +113,7 @@
112
113
  "vitest": "^3.2.3"
113
114
  },
114
115
  "engines": {
115
- "node": ">=18"
116
+ "node": ">=20"
116
117
  },
117
118
  "publishConfig": {
118
119
  "access": "public"
@@ -128,7 +129,7 @@
128
129
  }
129
130
  },
130
131
  "peerDependencies": {
131
- "@types/node": ">=18"
132
+ "@types/node": ">=20"
132
133
  },
133
134
  "peerDependenciesMeta": {
134
135
  "@types/node": {