pickem 0.1.0 → 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.
Files changed (114) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/README.md +64 -3
  3. package/checkbox.d.ts +3 -0
  4. package/checkbox.d.ts.map +1 -0
  5. package/checkbox.js +77 -0
  6. package/checkbox.js.map +1 -0
  7. package/core/ansi.d.ts +16 -0
  8. package/core/ansi.d.ts.map +1 -0
  9. package/core/ansi.js +21 -0
  10. package/core/ansi.js.map +1 -0
  11. package/core/chrome.d.ts +37 -0
  12. package/core/chrome.d.ts.map +1 -0
  13. package/core/chrome.js +51 -0
  14. package/core/chrome.js.map +1 -0
  15. package/core/cleanup.d.ts +5 -0
  16. package/core/cleanup.d.ts.map +1 -0
  17. package/core/cleanup.js +31 -0
  18. package/core/cleanup.js.map +1 -0
  19. package/core/colors.d.ts +24 -0
  20. package/core/colors.d.ts.map +1 -0
  21. package/core/colors.js +67 -0
  22. package/core/colors.js.map +1 -0
  23. package/core/hooks.d.ts +26 -0
  24. package/core/hooks.d.ts.map +1 -0
  25. package/core/hooks.js +80 -0
  26. package/core/hooks.js.map +1 -0
  27. package/core/index.d.ts +9 -0
  28. package/core/index.d.ts.map +1 -0
  29. package/core/index.js +9 -0
  30. package/core/index.js.map +1 -0
  31. package/core/key-reader.d.ts +6 -0
  32. package/core/key-reader.d.ts.map +1 -0
  33. package/core/key-reader.js +29 -0
  34. package/core/key-reader.js.map +1 -0
  35. package/core/keys.d.ts +17 -0
  36. package/core/keys.d.ts.map +1 -0
  37. package/core/keys.js +17 -0
  38. package/core/keys.js.map +1 -0
  39. package/core/pagination.d.ts +14 -0
  40. package/core/pagination.d.ts.map +1 -0
  41. package/core/pagination.js +36 -0
  42. package/core/pagination.js.map +1 -0
  43. package/core/runtime.d.ts +20 -0
  44. package/core/runtime.d.ts.map +1 -0
  45. package/core/runtime.js +74 -0
  46. package/core/runtime.js.map +1 -0
  47. package/core/screen.d.ts +14 -0
  48. package/core/screen.d.ts.map +1 -0
  49. package/core/screen.js +50 -0
  50. package/core/screen.js.map +1 -0
  51. package/core/status-glyph.d.ts +10 -0
  52. package/core/status-glyph.d.ts.map +1 -0
  53. package/core/status-glyph.js +18 -0
  54. package/core/status-glyph.js.map +1 -0
  55. package/core/symbols.d.ts +27 -0
  56. package/core/symbols.d.ts.map +1 -0
  57. package/core/symbols.js +41 -0
  58. package/core/symbols.js.map +1 -0
  59. package/core/testing.d.ts +15 -0
  60. package/core/testing.d.ts.map +1 -0
  61. package/core/testing.js +47 -0
  62. package/core/testing.js.map +1 -0
  63. package/core/theme.d.ts +14 -0
  64. package/core/theme.d.ts.map +1 -0
  65. package/core/theme.js +10 -0
  66. package/core/theme.js.map +1 -0
  67. package/core/width.d.ts +7 -0
  68. package/core/width.d.ts.map +1 -0
  69. package/core/width.js +46 -0
  70. package/core/width.js.map +1 -0
  71. package/display/format.d.ts +1 -3
  72. package/display/format.d.ts.map +1 -1
  73. package/display/format.js +4 -1
  74. package/display/format.js.map +1 -1
  75. package/index.d.ts +1 -1
  76. package/index.d.ts.map +1 -1
  77. package/internal/track.d.ts +3 -0
  78. package/internal/track.d.ts.map +1 -0
  79. package/internal/track.js +8 -0
  80. package/internal/track.js.map +1 -0
  81. package/package.json +17 -4
  82. package/pickem.d.ts +2 -0
  83. package/pickem.d.ts.map +1 -1
  84. package/pickem.js +37 -32
  85. package/pickem.js.map +1 -1
  86. package/prompts/checkbox.d.ts +14 -0
  87. package/prompts/checkbox.d.ts.map +1 -0
  88. package/prompts/checkbox.js +56 -0
  89. package/prompts/checkbox.js.map +1 -0
  90. package/prompts/confirm.d.ts +6 -0
  91. package/prompts/confirm.d.ts.map +1 -0
  92. package/prompts/confirm.js +32 -0
  93. package/prompts/confirm.js.map +1 -0
  94. package/prompts/input.d.ts +7 -0
  95. package/prompts/input.d.ts.map +1 -0
  96. package/prompts/input.js +52 -0
  97. package/prompts/input.js.map +1 -0
  98. package/prompts/search.d.ts +12 -0
  99. package/prompts/search.d.ts.map +1 -0
  100. package/prompts/search.js +67 -0
  101. package/prompts/search.js.map +1 -0
  102. package/prompts/searchable-checkbox.d.ts +14 -0
  103. package/prompts/searchable-checkbox.d.ts.map +1 -0
  104. package/prompts/searchable-checkbox.js +150 -0
  105. package/prompts/searchable-checkbox.js.map +1 -0
  106. package/prompts/select.d.ts +12 -0
  107. package/prompts/select.d.ts.map +1 -0
  108. package/prompts/select.js +40 -0
  109. package/prompts/select.js.map +1 -0
  110. package/types.d.ts +25 -2
  111. package/types.d.ts.map +1 -1
  112. package/wizard.d.ts.map +1 -1
  113. package/wizard.js +17 -5
  114. package/wizard.js.map +1 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,89 @@
1
+ # Changelog
2
+
3
+ All notable changes to pickem will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] — 2026-05-31
9
+
10
+ The picker engine is now fully native. The public API
11
+ (`pickem`, `pickem.from`, `pickem.checkbox`, `wizard`, `defineSource`) is
12
+ **unchanged and backward compatible** — existing callers need no changes. The
13
+ major bump reflects the engine replacement, the dependency removal, and the
14
+ refreshed prompt visuals.
15
+
16
+ ### Changed
17
+
18
+ - **Native picker engine.** Replaced `@inquirer/prompts` and `@inquirer/core`
19
+ with a built-in hook-based picker runtime (`createPicker`). The only
20
+ remaining runtime dependency is `string-width`.
21
+ - Prompt visuals adopt the Claude Code selector aesthetic — a cyan `❯` cursor
22
+ rail with truecolor accents and synchronized-output rendering.
23
+
24
+ ### Added
25
+
26
+ - `src/core/*` — native engine: hook runtime, screen buffer, raw key reader,
27
+ pagination, chrome/border rendering, theme tokens, color/accent helpers,
28
+ glyphs, display-width measurement, ANSI primitives, status glyphs, and
29
+ terminal cleanup.
30
+ - `src/prompts/*` — native implementations of select, input, confirm,
31
+ checkbox, search, and searchable-checkbox.
32
+ - Release quality gates wired into `npm run release`: `npm audit` (prod deps),
33
+ manifest audit (`scripts/audit.js`), `publint --strict`, `attw --pack`, and
34
+ an `npm pack` dry-run.
35
+
36
+ ### Removed
37
+
38
+ - Runtime dependencies `@inquirer/prompts` and `@inquirer/core`.
39
+ - Dev dependency `@inquirer/testing` — native prompts are now tested via the
40
+ in-tree `renderTest` harness (`src/core/testing.ts`).
41
+
42
+ ### Fixed
43
+
44
+ - `package.json` `exports` now lists the `types` condition first (export
45
+ conditions are order-sensitive), so TypeScript resolves declarations
46
+ correctly.
47
+
48
+ ## [0.2.0] — 2026-05-09
49
+
50
+ ### Added
51
+
52
+ - `pickem.checkbox(items, opts) → Promise<V[]>` — multi-select with built-in
53
+ filter-as-you-type. Built on `@inquirer/core`. Space toggles, Enter submits,
54
+ Backspace removes filter chars, Esc clears the filter. Pinned items respect
55
+ the filter (stay pinned within filtered set). Checked items remain in the
56
+ result even if the filter hides them. Result is in source order, matching
57
+ `@inquirer/prompts/checkbox` semantics.
58
+ - `searchable?: boolean` option on both `pickem()` and `pickem.checkbox()`.
59
+ Default `true`. When `false`, falls back to the non-search prompts
60
+ (`@inquirer/prompts/select` and `/checkbox` respectively).
61
+ - `'checkbox'` step type for `wizard()`. `ctx[step.id]` receives `string[]`.
62
+ - `allowFreeText?: boolean` on `pickem.checkbox()`. When `true`, typing text
63
+ that matches nothing and pressing Enter appends the typed text to the list
64
+ as a checked item; the filter clears and the prompt stays open.
65
+ - `npm run demo` — interactive tour of every feature using `tsx`.
66
+ Uses `MemoryStorage` so demo runs don't pollute `~/.pickem/usage.json`.
67
+ - New direct dep: `@inquirer/core ^10.3.2` (was already pulled transitively
68
+ by `@inquirer/prompts`).
69
+ - New devDeps: `@inquirer/testing ^3.3.5` (custom prompt smoke-tests),
70
+ `tsx ^4.21.0` (demo runner).
71
+ - Public type exports: `CheckboxOptions`, `CheckboxStep`.
72
+
73
+ ### Changed
74
+
75
+ - **BREAKING**: default `badgeStyle` changed from `'bracket'` to `'none'`.
76
+ Items with a `group` no longer render `[group]` badges by default. Pass
77
+ `{ badgeStyle: 'bracket' }` or `{ badgeStyle: 'dot' }` to opt back in.
78
+ - `searchableCheckbox` toggle glyphs upgraded from `[ ]`/`[x]` to filled
79
+ circles (`○`/`●`, green when checked) with a cyan `❯` cursor. Matches
80
+ the Apple/iOS and `@inquirer/prompts` default visual style.
81
+ - `pickem()` single-select stats lookup no longer reaches into the tracker's
82
+ private `cache` field — uses the public `getStats()` method instead.
83
+ Fixes a latent issue when the tracker hadn't been loaded yet.
84
+ - `BadgeStyle` union now includes `'none'`.
85
+
86
+ ### Internal
87
+
88
+ - Extracted `resolveTrackOptions` to `src/internal/track.ts` (shared between
89
+ `pickem.ts` and `checkbox.ts`).
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # pickem
2
2
 
3
- Usage-sorted searchable autocomplete for CLI tools. One dependency-light module that gives every CLI the same interactive picker pattern: discover items, sort by usage, search, select, track.
3
+ Usage-sorted searchable autocomplete for CLI tools. One near-zero-dependency module that gives every CLI the same interactive picker pattern: discover items, sort by usage, search, select, track.
4
+
5
+ pickem runs on its **own native picker engine** — no `@inquirer`, no prompt-library dependency. The only runtime dependency is [`string-width`](https://github.com/sindresorhus/string-width) (for correct CJK/emoji column width). The look is a Claude Code `/skills`-style selector: an accent title, a dim hint line, a rounded search box, columnar rows with a `❯` chevron on the active item, and a `↓ N more below` footer — truecolor with graceful 256/ASCII fallbacks. See [`docs/standards/selector-ui.md`](docs/standards/selector-ui.md) and [`docs/architecture/picker-engine.md`](docs/architecture/picker-engine.md).
4
6
 
5
7
  ## Install
6
8
 
@@ -8,6 +10,15 @@ Usage-sorted searchable autocomplete for CLI tools. One dependency-light module
8
10
  npm install pickem
9
11
  ```
10
12
 
13
+ ## Demo
14
+
15
+ An interactive tour of every feature ships with the repo:
16
+
17
+ ```bash
18
+ git clone https://github.com/calebogden/pickem
19
+ cd pickem && npm install && npm run demo
20
+ ```
21
+
11
22
  ## Quick Start
12
23
 
13
24
  ```typescript
@@ -20,6 +31,35 @@ const choice = await pickem([
20
31
  ])
21
32
  ```
22
33
 
34
+ ## Multi-select (checkbox)
35
+
36
+ `pickem.checkbox(items, opts)` returns `string[]` — the values of every checked item.
37
+
38
+ ```typescript
39
+ import { pickem } from 'pickem'
40
+
41
+ const choices = await pickem.checkbox([
42
+ { label: 'Deploy', value: 'deploy' },
43
+ { label: 'Test', value: 'test' },
44
+ { label: 'Lint', value: 'lint' },
45
+ ], { message: 'Which steps?', defaultChecked: ['test'] })
46
+ // => ['deploy', 'test']
47
+ ```
48
+
49
+ Key bindings: Space toggles a selection, Enter submits, type to filter, Esc clears the filter, Backspace removes filter characters.
50
+
51
+ Pass `searchable: false` to fall back to the plain native checkbox picker (no search bar):
52
+
53
+ ```typescript
54
+ const choices = await pickem.checkbox(items, { searchable: false })
55
+ ```
56
+
57
+ Pass `{ allowFreeText: true }` to let the user add ad-hoc entries: type text that doesn't match any item, press Enter, and the typed text is appended to the list as a checked item. The filter clears and the prompt stays open so they can keep toggling or add more.
58
+
59
+ ```typescript
60
+ const tags = await pickem.checkbox(KNOWN_TAGS, { allowFreeText: true })
61
+ ```
62
+
23
63
  ## Usage Tracking
24
64
 
25
65
  Items sort by how often they're used. Enable with `track: true`:
@@ -50,7 +90,7 @@ const npm = defineSource('npm', async () => [
50
90
  ])
51
91
 
52
92
  const choice = await pickem.from([scripts, npm], { track: true })
53
- // Items get group badges: [scripts], [npm]
93
+ // Items get group badges when badgeStyle is set: [scripts], [npm]
54
94
  ```
55
95
 
56
96
  ## Wizard Flows
@@ -76,6 +116,7 @@ const result = await wizard([
76
116
  | Type | Purpose |
77
117
  |------|---------|
78
118
  | `pick` | Searchable usage-sorted selection |
119
+ | `checkbox` | Searchable multi-select, returns an array |
79
120
  | `select` | Simple choice list |
80
121
  | `input` | Free text entry |
81
122
  | `confirm` | Yes/no |
@@ -104,16 +145,36 @@ await pickem(items, {
104
145
  message: 'Pick one:', // Prompt message
105
146
  pageSize: 15, // Visible items
106
147
  track: true | TrackOptions, // Enable usage tracking
148
+ searchable: true, // Set false to skip the search bar (falls back to select)
107
149
  searchFields: ['label', 'description'], // Fields to search (dot-notation for meta)
108
150
  search: (item, term) => boolean, // Custom search function
109
151
  format: (item, stats) => string, // Custom display formatter
110
- badgeStyle: 'bracket' | 'dot' | fn, // Group badge style
152
+ badgeStyle: 'none' | 'bracket' | 'dot' | fn, // Group badge style (default: 'none')
111
153
  badgeColors: { npm: 'red' }, // Badge color overrides
112
154
  sort: (a, b) => number, // Tiebreaker after usage sort
113
155
  onSelect: (item) => {}, // Post-selection callback
156
+ allowFreeText: true, // Surface unmatched input as a selectable option
157
+ })
158
+ ```
159
+
160
+ `pickem.checkbox` accepts the same options, plus:
161
+
162
+ ```typescript
163
+ await pickem.checkbox(items, {
164
+ required: false, // Require at least one selection before Enter submits
165
+ defaultChecked: ['test'], // Pre-checked values
166
+ allowFreeText: true, // Type unmatched text + Enter to add as a checked item
114
167
  })
115
168
  ```
116
169
 
170
+ ### `badgeStyle` default changed in v0.2.0
171
+
172
+ The default `badgeStyle` is now `'none'` — group labels are hidden unless you opt in. This is a breaking change if your code relied on the old default of `'bracket'`. To restore the previous behavior:
173
+
174
+ ```typescript
175
+ await pickem(items, { badgeStyle: 'bracket' })
176
+ ```
177
+
117
178
  ## Requirements
118
179
 
119
180
  - Node >= 18
package/checkbox.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { CheckboxOptions, PickItem } from './types.js';
2
+ export declare function checkbox<V = string>(items: PickItem<V>[], options?: CheckboxOptions<V>): Promise<V[]>;
3
+ //# sourceMappingURL=checkbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkbox.d.ts","sourceRoot":"","sources":["../src/checkbox.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAc,MAAM,YAAY,CAAA;AAQvE,wBAAsB,QAAQ,CAAC,CAAC,GAAG,MAAM,EACvC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,EACpB,OAAO,GAAE,eAAe,CAAC,CAAC,CAAM,GAC/B,OAAO,CAAC,CAAC,EAAE,CAAC,CAwEd"}
package/checkbox.js ADDED
@@ -0,0 +1,77 @@
1
+ import { checkboxPicker as standardCheckbox } from './prompts/checkbox.js';
2
+ import { UsageTracker } from './usage/tracker.js';
3
+ import { createFormatter } from './display/format.js';
4
+ import { createSearchFn } from './search/search.js';
5
+ import { resolveTrackOptions } from './internal/track.js';
6
+ import { searchableCheckbox } from './prompts/searchable-checkbox.js';
7
+ export async function checkbox(items, options = {}) {
8
+ const trackOpts = resolveTrackOptions(options.track);
9
+ const tracker = trackOpts ? new UsageTracker(trackOpts) : null;
10
+ const keyFn = trackOpts?.key ?? ((item) => item.label);
11
+ let sorted;
12
+ if (tracker) {
13
+ sorted = await tracker.sortItems(items, keyFn, options.sort);
14
+ }
15
+ else if (options.sort) {
16
+ sorted = [...items].sort(options.sort);
17
+ }
18
+ else {
19
+ sorted = items;
20
+ }
21
+ const formatFn = options.format ??
22
+ createFormatter({
23
+ badgeStyle: options.badgeStyle,
24
+ badgeColors: options.badgeColors,
25
+ });
26
+ const searchFn = options.search ?? createSearchFn(options.searchFields);
27
+ const statsMap = new Map();
28
+ if (tracker) {
29
+ for (const item of sorted) {
30
+ statsMap.set(item.value, await tracker.getStats(keyFn(item)));
31
+ }
32
+ }
33
+ // Build a value→item map once for O(n) post-submit lookups
34
+ const valueToItem = new Map(sorted.map((item) => [item.value, item]));
35
+ const useSearchable = options.searchable !== false;
36
+ let result;
37
+ if (useSearchable) {
38
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
+ result = (await searchableCheckbox({
40
+ message: options.message ?? 'Pick:',
41
+ items: sorted,
42
+ pageSize: options.pageSize ?? 15,
43
+ required: options.required ?? false,
44
+ defaultChecked: (options.defaultChecked ?? []),
45
+ allowFreeText: options.allowFreeText ?? false,
46
+ searchFn: searchFn,
47
+ formatFn: formatFn,
48
+ usageStats: tracker ? statsMap : null,
49
+ }));
50
+ }
51
+ else {
52
+ const defaultCheckedSet = new Set(options.defaultChecked ?? []);
53
+ result = (await standardCheckbox({
54
+ message: options.message ?? 'Pick:',
55
+ pageSize: options.pageSize ?? 15,
56
+ required: options.required ?? false,
57
+ choices: sorted.map((item) => ({
58
+ name: formatFn(item, statsMap.get(item.value) ?? null),
59
+ value: item.value,
60
+ checked: defaultCheckedSet.has(item.value),
61
+ description: undefined, // We handle description in formatFn
62
+ })),
63
+ }));
64
+ }
65
+ // Single combined pass — track usage and fire onSelect for each selected item
66
+ for (const v of result) {
67
+ const item = valueToItem.get(v);
68
+ if (!item)
69
+ continue;
70
+ if (tracker)
71
+ await tracker.track(keyFn(item), trackOpts?.source);
72
+ if (options.onSelect)
73
+ await options.onSelect(item);
74
+ }
75
+ return result;
76
+ }
77
+ //# sourceMappingURL=checkbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkbox.js","sourceRoot":"","sources":["../src/checkbox.ts"],"names":[],"mappings":"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"}
package/core/ansi.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ export declare const ansi: {
2
+ readonly cursorUp: (n?: number) => string;
3
+ readonly cursorDown: (n?: number) => string;
4
+ readonly cursorTo: (col: number) => string;
5
+ readonly hideCursor: "\u001B[?25l";
6
+ readonly showCursor: "\u001B[?25h";
7
+ readonly eraseLine: "\u001B[2K";
8
+ readonly eraseDown: "\u001B[0J";
9
+ readonly eraseScreen: "\u001B[2J";
10
+ readonly beginSync: "\u001B[?2026h";
11
+ readonly endSync: "\u001B[?2026l";
12
+ readonly enterAltScreen: "\u001B[?1049h";
13
+ readonly exitAltScreen: "\u001B[?1049l";
14
+ readonly link: (url: string, text: string) => string;
15
+ };
16
+ //# sourceMappingURL=ansi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ansi.d.ts","sourceRoot":"","sources":["../../src/core/ansi.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,IAAI;;;6BAGC,MAAM;;;;;;;;;;yBAYV,MAAM,QAAQ,MAAM;CACxB,CAAA"}
package/core/ansi.js ADDED
@@ -0,0 +1,21 @@
1
+ // src/core/ansi.ts
2
+ const ESC = '\x1b';
3
+ const CSI = `${ESC}[`;
4
+ export const ansi = {
5
+ cursorUp: (n = 1) => `${CSI}${n}A`,
6
+ cursorDown: (n = 1) => `${CSI}${n}B`,
7
+ cursorTo: (col) => `${CSI}${col + 1}G`, // 0-based → 1-based column
8
+ hideCursor: `${CSI}?25l`,
9
+ showCursor: `${CSI}?25h`,
10
+ eraseLine: `${CSI}2K`,
11
+ eraseDown: `${CSI}0J`,
12
+ eraseScreen: `${CSI}2J`,
13
+ // DEC private mode 2026 — atomic frame repaint (anti-flicker); ignored if unsupported
14
+ beginSync: `${CSI}?2026h`,
15
+ endSync: `${CSI}?2026l`,
16
+ enterAltScreen: `${CSI}?1049h`,
17
+ exitAltScreen: `${CSI}?1049l`,
18
+ // OSC-8 hyperlink — degrades to plain text on unsupporting terminals
19
+ link: (url, text) => `${ESC}]8;;${url}\x07${text}${ESC}]8;;\x07`,
20
+ };
21
+ //# sourceMappingURL=ansi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ansi.js","sourceRoot":"","sources":["../../src/core/ansi.ts"],"names":[],"mappings":"AAAA,mBAAmB;AACnB,MAAM,GAAG,GAAG,MAAM,CAAA;AAClB,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;AAErB,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG;IAClC,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG;IACpC,QAAQ,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,EAAE,2BAA2B;IAC3E,UAAU,EAAE,GAAG,GAAG,MAAM;IACxB,UAAU,EAAE,GAAG,GAAG,MAAM;IACxB,SAAS,EAAE,GAAG,GAAG,IAAI;IACrB,SAAS,EAAE,GAAG,GAAG,IAAI;IACrB,WAAW,EAAE,GAAG,GAAG,IAAI;IACvB,sFAAsF;IACtF,SAAS,EAAE,GAAG,GAAG,QAAQ;IACzB,OAAO,EAAE,GAAG,GAAG,QAAQ;IACvB,cAAc,EAAE,GAAG,GAAG,QAAQ;IAC9B,aAAa,EAAE,GAAG,GAAG,QAAQ;IAC7B,qEAAqE;IACrE,IAAI,EAAE,CAAC,GAAW,EAAE,IAAY,EAAE,EAAE,CAAC,GAAG,GAAG,OAAO,GAAG,OAAO,IAAI,GAAG,GAAG,UAAU;CACxE,CAAA"}
@@ -0,0 +1,37 @@
1
+ import type { Colors } from './colors.js';
2
+ import type { Symbols } from './symbols.js';
3
+ export interface ChromeDeps {
4
+ colors: Colors;
5
+ symbols: Symbols;
6
+ }
7
+ export interface RowArgs {
8
+ label: string;
9
+ meta?: string[];
10
+ active?: boolean;
11
+ glyph?: string;
12
+ state?: string;
13
+ maxWidth?: number;
14
+ }
15
+ export interface SearchBoxArgs {
16
+ value: string;
17
+ placeholder?: string;
18
+ focused?: boolean;
19
+ width?: number;
20
+ }
21
+ export interface FooterArgs {
22
+ moreAbove?: number;
23
+ moreBelow?: number;
24
+ summary?: string;
25
+ }
26
+ export declare function makeChrome({ colors, symbols }: ChromeDeps): {
27
+ /** Accent+bold title, then a dim "·"-joined hint line. */
28
+ header(title: string, hints?: string[]): string;
29
+ /** Rounded boxed search input (searchable pickers only). */
30
+ searchBox({ value, placeholder, focused, width }: SearchBoxArgs): string;
31
+ /** One columnar list row: pointer [glyph] [state] name · dim-meta. */
32
+ row({ label, meta, active, glyph, state, maxWidth }: RowArgs): string;
33
+ /** Dim pagination/summary footer. */
34
+ footer({ moreAbove, moreBelow, summary }: FooterArgs): string;
35
+ };
36
+ export type Chrome = ReturnType<typeof makeChrome>;
37
+ //# sourceMappingURL=chrome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chrome.d.ts","sourceRoot":"","sources":["../../src/core/chrome.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAG3C,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAOD,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,UAAU;IAKtD,0DAA0D;kBAC5C,MAAM,UAAS,MAAM,EAAE,GAAQ,MAAM;IAMnD,4DAA4D;sDACO,aAAa,GAAG,MAAM;IAYzF,sEAAsE;yDACW,OAAO,GAAG,MAAM;IASjG,qCAAqC;8CACkB,UAAU,GAAG,MAAM;EAQ7E;AAED,MAAM,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAA"}
package/core/chrome.js ADDED
@@ -0,0 +1,51 @@
1
+ import { displayWidth, truncateToWidth } from './width.js';
2
+ function padToWidth(str, width) {
3
+ const w = displayWidth(str);
4
+ return w >= width ? str : str + ' '.repeat(width - w);
5
+ }
6
+ export function makeChrome({ colors, symbols }) {
7
+ const dot = colors.dim(' · ');
8
+ const accent = colors.accent;
9
+ return {
10
+ /** Accent+bold title, then a dim "·"-joined hint line. */
11
+ header(title, hints = []) {
12
+ const head = colors.bold(accent(title));
13
+ if (hints.length === 0)
14
+ return head;
15
+ return `${head}\n${colors.dim(hints.join(' · '))}`;
16
+ },
17
+ /** Rounded boxed search input (searchable pickers only). */
18
+ searchBox({ value, placeholder = '', focused = true, width = 40 }) {
19
+ const w = Math.max(20, Math.min(width, 60));
20
+ const cursor = focused ? colors.inverse(' ') : '';
21
+ const text = value.length ? value + cursor : colors.dim(placeholder) + cursor;
22
+ const content = `${colors.dim(symbols.search)} ${text}`;
23
+ const inner = padToWidth(content, w);
24
+ const top = colors.dim(symbols.cornerTL + symbols.line.repeat(w + 1) + symbols.cornerTR);
25
+ const mid = `${colors.dim(symbols.boxV)} ${inner}${colors.dim(symbols.boxV)}`;
26
+ const bot = colors.dim(symbols.cornerBL + symbols.line.repeat(w + 1) + symbols.cornerBR);
27
+ return `${top}\n${mid}\n${bot}`;
28
+ },
29
+ /** One columnar list row: pointer [glyph] [state] name · dim-meta. */
30
+ row({ label, meta = [], active = false, glyph = '', state = '', maxWidth = 80 }) {
31
+ const pointer = active ? accent(symbols.pointer) : ' ';
32
+ const g = glyph ? `${glyph} ` : '';
33
+ const s = state ? `${colors.dim(state)} ` : '';
34
+ const name = active ? accent(truncateToWidth(label, maxWidth)) : truncateToWidth(label, maxWidth);
35
+ const m = meta.length ? dot + colors.dim(meta.join(' · ')) : '';
36
+ return `${pointer} ${g}${s}${name}${m}`;
37
+ },
38
+ /** Dim pagination/summary footer. */
39
+ footer({ moreAbove = 0, moreBelow = 0, summary = '' }) {
40
+ const bits = [];
41
+ if (moreAbove)
42
+ bits.push(colors.dim(`${symbols.moreAbove} ${moreAbove} more above`));
43
+ if (moreBelow)
44
+ bits.push(colors.dim(`${symbols.moreBelow} ${moreBelow} more below`));
45
+ if (summary)
46
+ bits.push(colors.dim(summary));
47
+ return bits.join(' ');
48
+ },
49
+ };
50
+ }
51
+ //# sourceMappingURL=chrome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chrome.js","sourceRoot":"","sources":["../../src/core/chrome.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ type Handler = () => void;
2
+ /** Register a terminal-restore handler that runs on any process exit path. */
3
+ export declare function onExit(fn: Handler): () => void;
4
+ export {};
5
+ //# sourceMappingURL=cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/core/cleanup.ts"],"names":[],"mappings":"AAAA,KAAK,OAAO,GAAG,MAAM,IAAI,CAAA;AA0BzB,8EAA8E;AAC9E,wBAAgB,MAAM,CAAC,EAAE,EAAE,OAAO,GAAG,MAAM,IAAI,CAI9C"}
@@ -0,0 +1,31 @@
1
+ const handlers = new Set();
2
+ let installed = false;
3
+ function fireAll() {
4
+ for (const h of [...handlers]) {
5
+ try {
6
+ h();
7
+ }
8
+ catch {
9
+ /* never throw during teardown */
10
+ }
11
+ }
12
+ }
13
+ function install() {
14
+ if (installed)
15
+ return;
16
+ installed = true;
17
+ process.once('exit', fireAll);
18
+ for (const sig of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
19
+ process.once(sig, () => {
20
+ fireAll();
21
+ process.exit(sig === 'SIGINT' ? 130 : 1);
22
+ });
23
+ }
24
+ }
25
+ /** Register a terminal-restore handler that runs on any process exit path. */
26
+ export function onExit(fn) {
27
+ install();
28
+ handlers.add(fn);
29
+ return () => handlers.delete(fn);
30
+ }
31
+ //# sourceMappingURL=cleanup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../../src/core/cleanup.ts"],"names":[],"mappings":"AACA,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAW,CAAA;AACnC,IAAI,SAAS,GAAG,KAAK,CAAA;AAErB,SAAS,OAAO;IACd,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,CAAC,EAAE,CAAA;QACL,CAAC;QAAC,MAAM,CAAC;YACP,iCAAiC;QACnC,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,OAAO;IACd,IAAI,SAAS;QAAE,OAAM;IACrB,SAAS,GAAG,IAAI,CAAA;IAChB,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,KAAK,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAU,EAAE,CAAC;QAC3D,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;YACrB,OAAO,EAAE,CAAA;YACT,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC1C,CAAC,CAAC,CAAA;IACJ,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,MAAM,CAAC,EAAW;IAChC,OAAO,EAAE,CAAA;IACT,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAChB,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAClC,CAAC"}
@@ -0,0 +1,24 @@
1
+ export interface ColorOptions {
2
+ level?: 0 | 1 | 2 | 3;
3
+ env?: NodeJS.ProcessEnv;
4
+ isTTY?: boolean;
5
+ }
6
+ export declare function detectLevel(env: NodeJS.ProcessEnv, isTTY: boolean): 0 | 1 | 2 | 3;
7
+ export declare function makeColors(opts?: ColorOptions): {
8
+ level: 0 | 1 | 2 | 3;
9
+ bold: (s: string) => string;
10
+ dim: (s: string) => string;
11
+ italic: (s: string) => string;
12
+ underline: (s: string) => string;
13
+ inverse: (s: string) => string;
14
+ cyan: (s: string) => string;
15
+ green: (s: string) => string;
16
+ red: (s: string) => string;
17
+ yellow: (s: string) => string;
18
+ gray: (s: string) => string;
19
+ accent: (s: string) => string;
20
+ rgb: (r: number, g: number, b: number) => (s: string) => string;
21
+ gradient: (from: [number, number, number], to: [number, number, number]) => (s: string) => string;
22
+ };
23
+ export type Colors = ReturnType<typeof makeColors>;
24
+ //# sourceMappingURL=colors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../../src/core/colors.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAajF;AAQD,wBAAgB,UAAU,CAAC,IAAI,GAAE,YAAiB;;cAEb,MAAM;aAAN,MAAM;gBAAN,MAAM;mBAAN,MAAM;iBAAN,MAAM;cAAN,MAAM;eAAN,MAAM;aAAN,MAAM;gBAAN,MAAM;cAAN,MAAM;gBACY,MAAM;aAA3C,MAAM,KAAK,MAAM,KAAK,MAAM,MAAM,GAAG,MAAM;qBAMnC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM;EA6B9F;AAED,MAAM,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAA"}
package/core/colors.js ADDED
@@ -0,0 +1,67 @@
1
+ export function detectLevel(env, isTTY) {
2
+ if ('NO_COLOR' in env && env.NO_COLOR !== '')
3
+ return 0;
4
+ if (env.FORCE_COLOR != null) {
5
+ const n = Number(env.FORCE_COLOR);
6
+ return (Number.isNaN(n) ? 1 : Math.max(0, Math.min(3, n)));
7
+ }
8
+ if (!isTTY)
9
+ return 0;
10
+ const ct = env.COLORTERM ?? '';
11
+ if (ct === 'truecolor' || ct === '24bit')
12
+ return 3;
13
+ const term = env.TERM ?? '';
14
+ if (/-256(color)?$/.test(term))
15
+ return 2;
16
+ if (term && term !== 'dumb')
17
+ return 1;
18
+ return 0;
19
+ }
20
+ // Map an 8-bit RGB triple to the closest xterm-256 cube index.
21
+ function rgbTo256(r, g, b) {
22
+ const q = (v) => (v < 48 ? 0 : v < 115 ? 1 : Math.round((v - 35) / 40));
23
+ return 16 + 36 * q(r) + 6 * q(g) + q(b);
24
+ }
25
+ export function makeColors(opts = {}) {
26
+ const level = opts.level ?? detectLevel(opts.env ?? process.env, opts.isTTY ?? !!process.stdout.isTTY);
27
+ const wrap = (open) => (s) => (level === 0 ? s : `\x1b[${open}m${s}\x1b[0m`);
28
+ const rgb = (r, g, b) => (s) => {
29
+ if (level === 0)
30
+ return s;
31
+ if (level >= 3)
32
+ return `\x1b[38;2;${r};${g};${b}m${s}\x1b[0m`;
33
+ return `\x1b[38;5;${rgbTo256(r, g, b)}m${s}\x1b[0m`;
34
+ };
35
+ // Per-character truecolor interpolation (the Vite/Astro banner look).
36
+ const gradient = (from, to) => (s) => {
37
+ if (level === 0)
38
+ return s;
39
+ const chars = [...s];
40
+ return chars
41
+ .map((ch, i) => {
42
+ const t = chars.length <= 1 ? 0 : i / (chars.length - 1);
43
+ const c = [0, 1, 2].map((k) => Math.round(from[k] + (to[k] - from[k]) * t));
44
+ return rgb(c[0], c[1], c[2])(ch);
45
+ })
46
+ .join('');
47
+ };
48
+ return {
49
+ level,
50
+ bold: wrap('1'),
51
+ dim: wrap('2'),
52
+ italic: wrap('3'),
53
+ underline: wrap('4'),
54
+ inverse: wrap('7'),
55
+ cyan: wrap('36'),
56
+ green: wrap('32'),
57
+ red: wrap('31'),
58
+ yellow: wrap('33'),
59
+ gray: wrap('90'),
60
+ // pickem accent — lavender/violet (selector UI, see docs/standards/selector-ui.md).
61
+ // truecolor → 256 fallback is handled by rgb().
62
+ accent: rgb(180, 160, 250),
63
+ rgb,
64
+ gradient,
65
+ };
66
+ }
67
+ //# sourceMappingURL=colors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.js","sourceRoot":"","sources":["../../src/core/colors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { KeyEvent } from './keys.js';
2
+ import type { PickerInput } from './runtime.js';
3
+ interface Slot {
4
+ value?: unknown;
5
+ deps?: unknown[];
6
+ cleanup?: () => void;
7
+ }
8
+ export interface HookStore {
9
+ slots: Slot[];
10
+ cursor: number;
11
+ scheduleRender: () => void;
12
+ keypressHandlers: ((key: KeyEvent, input: PickerInput) => void)[];
13
+ effects: (() => void)[];
14
+ reset(): void;
15
+ }
16
+ export declare function createHookStore(scheduleRender: () => void): HookStore;
17
+ export declare function withHookStore<T>(store: HookStore, fn: () => T): T;
18
+ export declare function useState<T>(initial: T): [T, (next: T) => void];
19
+ export declare function useRef<T>(initial: T): {
20
+ current: T;
21
+ };
22
+ export declare function useMemo<T>(factory: () => T, deps: unknown[]): T;
23
+ export declare function useEffect(effect: () => void | (() => void), deps: unknown[]): void;
24
+ export declare function useKeypress(handler: (key: KeyEvent, input: PickerInput) => void): void;
25
+ export {};
26
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/core/hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE/C,UAAU,IAAI;IACZ,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,IAAI,CAAC,EAAE,OAAO,EAAE,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,IAAI,EAAE,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,gBAAgB,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC,EAAE,CAAA;IACjE,OAAO,EAAE,CAAC,MAAM,IAAI,CAAC,EAAE,CAAA;IACvB,KAAK,IAAI,IAAI,CAAA;CACd;AAID,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,IAAI,GAAG,SAAS,CAcrE;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAEjE;AAeD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,CAU9D;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG;IAAE,OAAO,EAAE,CAAC,CAAA;CAAE,CAIpD;AAOD,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,CAO/D;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAWlF;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI,CAEtF"}