@emeryld/manager 1.5.3 → 1.5.4
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/dist/helper-cli/multi-select.js +37 -21
- package/dist/helper-cli/prompts.js +30 -21
- package/package.json +3 -2
|
@@ -4,6 +4,25 @@ import { colors, getEntryColor } from './colors.js';
|
|
|
4
4
|
import { pageHeading } from './display.js';
|
|
5
5
|
import { BACK_KEY, buildVisibleOptions, NEXT_KEY, PAGE_SIZE, PREVIOUS_KEY, } from './pagination.js';
|
|
6
6
|
import { findScriptEntry } from './scripts.js';
|
|
7
|
+
const ANSI_REGEX = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
|
|
8
|
+
function visibleLength(text) {
|
|
9
|
+
const clean = text.replace(ANSI_REGEX, '');
|
|
10
|
+
let length = 0;
|
|
11
|
+
for (const ch of clean) {
|
|
12
|
+
const code = ch.codePointAt(0) ?? 0;
|
|
13
|
+
length += code > 0xffff ? 2 : 1;
|
|
14
|
+
}
|
|
15
|
+
return length;
|
|
16
|
+
}
|
|
17
|
+
function highlightHoveredLine(text) {
|
|
18
|
+
const columns = process.stdout.columns ?? 80;
|
|
19
|
+
const len = visibleLength(text);
|
|
20
|
+
// Avoid padding to the exact terminal width; that can trigger hard-wrap drift
|
|
21
|
+
// during repeated redraws in some terminals.
|
|
22
|
+
const targetWidth = Math.max(1, columns - 1);
|
|
23
|
+
const padded = len < targetWidth ? `${text}${' '.repeat(targetWidth - len)}` : text;
|
|
24
|
+
return `\x1b[7m${padded}\x1b[0m`;
|
|
25
|
+
}
|
|
7
26
|
function formatInteractiveLines(state, selectedIndex, selectedEntries, title, searchState) {
|
|
8
27
|
const heading = pageHeading(title, state.page, state.pageCount);
|
|
9
28
|
const lines = [heading];
|
|
@@ -14,18 +33,28 @@ function formatInteractiveLines(state, selectedIndex, selectedEntries, title, se
|
|
|
14
33
|
}
|
|
15
34
|
state.options.forEach((option, index) => {
|
|
16
35
|
const isSelectedRow = index === selectedIndex;
|
|
17
|
-
const pointer = isSelectedRow ?
|
|
36
|
+
const pointer = isSelectedRow ? '➤ ' : '';
|
|
18
37
|
const entryColorizer = option.type === 'entry' && option.entry.color
|
|
19
38
|
? getEntryColor(option.entry)
|
|
20
39
|
: colors.cyan;
|
|
21
|
-
const numberLabelColorizer = isSelectedRow ?
|
|
40
|
+
const numberLabelColorizer = isSelectedRow ? (text) => text : entryColorizer;
|
|
22
41
|
const numberLabel = numberLabelColorizer(`${option.hotkey}`.padStart(2, ' '));
|
|
23
42
|
if (option.type === 'entry') {
|
|
24
43
|
const isChecked = selectedEntries.has(option.entry);
|
|
25
|
-
const checkbox = isChecked
|
|
26
|
-
|
|
44
|
+
const checkbox = isChecked
|
|
45
|
+
? isSelectedRow
|
|
46
|
+
? '[x]'
|
|
47
|
+
: colors.green('[x]')
|
|
48
|
+
: isSelectedRow
|
|
49
|
+
? '[ ]'
|
|
50
|
+
: colors.dim('[ ]');
|
|
51
|
+
const labelColorizer = isSelectedRow ? (text) => text : entryColorizer;
|
|
27
52
|
const label = labelColorizer(option.entry.displayName);
|
|
28
|
-
|
|
53
|
+
const metaLabel = isSelectedRow
|
|
54
|
+
? option.entry.metaLabel
|
|
55
|
+
: colors.dim(option.entry.metaLabel);
|
|
56
|
+
const row = `${pointer}${numberLabel}. ${checkbox} ${option.entry.emoji} ${label} ${metaLabel}`;
|
|
57
|
+
lines.push(isSelectedRow ? highlightHoveredLine(row) : row);
|
|
29
58
|
return;
|
|
30
59
|
}
|
|
31
60
|
const icon = option.action === 'back'
|
|
@@ -38,12 +67,9 @@ function formatInteractiveLines(state, selectedIndex, selectedEntries, title, se
|
|
|
38
67
|
: option.action === 'next'
|
|
39
68
|
? 'Next page'
|
|
40
69
|
: 'Back';
|
|
41
|
-
const navLabel =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
? baseLabel
|
|
45
|
-
: colors.dim(baseLabel);
|
|
46
|
-
lines.push(`${pointer}${numberLabel}. ${icon} ${navLabel}`);
|
|
70
|
+
const navLabel = option.enabled ? baseLabel : colors.dim(baseLabel);
|
|
71
|
+
const row = `${pointer}${numberLabel}. ${icon} ${navLabel}`;
|
|
72
|
+
lines.push(isSelectedRow ? highlightHoveredLine(row) : row);
|
|
47
73
|
});
|
|
48
74
|
if (searchState?.active && !searchState.hasResults) {
|
|
49
75
|
lines.push(colors.yellow('No scripts match your search.'));
|
|
@@ -54,16 +80,6 @@ function formatInteractiveLines(state, selectedIndex, selectedEntries, title, se
|
|
|
54
80
|
}
|
|
55
81
|
function renderInteractiveList(lines, previousLineCount) {
|
|
56
82
|
const columns = process.stdout.columns ?? 80;
|
|
57
|
-
const ansiRegex = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
|
|
58
|
-
const visibleLength = (text) => {
|
|
59
|
-
const clean = text.replace(ansiRegex, '');
|
|
60
|
-
let length = 0;
|
|
61
|
-
for (const ch of clean) {
|
|
62
|
-
const code = ch.codePointAt(0) ?? 0;
|
|
63
|
-
length += code > 0xffff ? 2 : 1;
|
|
64
|
-
}
|
|
65
|
-
return length;
|
|
66
|
-
};
|
|
67
83
|
const nextLineCount = lines.reduce((total, line) => {
|
|
68
84
|
const len = visibleLength(line);
|
|
69
85
|
const wrapped = Math.max(1, Math.ceil(len / columns));
|
|
@@ -53,6 +53,25 @@ async function promptWithReadline(entries, title) {
|
|
|
53
53
|
rl.close();
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
+
const ANSI_REGEX = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
|
|
57
|
+
function visibleLength(text) {
|
|
58
|
+
const clean = text.replace(ANSI_REGEX, '');
|
|
59
|
+
let length = 0;
|
|
60
|
+
for (const ch of clean) {
|
|
61
|
+
const code = ch.codePointAt(0) ?? 0;
|
|
62
|
+
length += code > 0xffff ? 2 : 1;
|
|
63
|
+
}
|
|
64
|
+
return length;
|
|
65
|
+
}
|
|
66
|
+
function highlightHoveredLine(text) {
|
|
67
|
+
const columns = process.stdout.columns ?? 80;
|
|
68
|
+
const len = visibleLength(text);
|
|
69
|
+
// Avoid padding to the exact terminal width; that can trigger hard-wrap drift
|
|
70
|
+
// during repeated redraws in some terminals.
|
|
71
|
+
const targetWidth = Math.max(1, columns - 1);
|
|
72
|
+
const padded = len < targetWidth ? `${text}${' '.repeat(targetWidth - len)}` : text;
|
|
73
|
+
return `\x1b[7m${padded}\x1b[0m`;
|
|
74
|
+
}
|
|
56
75
|
function formatInteractiveLines(state, selectedIndex, title, searchState) {
|
|
57
76
|
const heading = pageHeading(title, state.page, state.pageCount);
|
|
58
77
|
const lines = [heading];
|
|
@@ -62,21 +81,24 @@ function formatInteractiveLines(state, selectedIndex, title, searchState) {
|
|
|
62
81
|
}
|
|
63
82
|
state.options.forEach((option, index) => {
|
|
64
83
|
const isSelected = index === selectedIndex;
|
|
65
|
-
const pointer = isSelected ?
|
|
84
|
+
const pointer = isSelected ? '➤ ' : '';
|
|
66
85
|
const entryColorizer = option.type === 'entry' && option.entry.color
|
|
67
86
|
? getEntryColor(option.entry)
|
|
68
87
|
: colors.cyan;
|
|
69
|
-
const numberLabelColorizer = isSelected ?
|
|
88
|
+
const numberLabelColorizer = isSelected ? (text) => text : entryColorizer;
|
|
70
89
|
const numberLabel = numberLabelColorizer(`${option.hotkey}`.padStart(2, ' '));
|
|
71
90
|
if (option.type === 'entry') {
|
|
72
|
-
const labelColorizer = isSelected ?
|
|
91
|
+
const labelColorizer = isSelected ? (text) => text : entryColorizer;
|
|
73
92
|
const label = labelColorizer(option.entry.displayName);
|
|
74
93
|
const runHint = searchState?.active &&
|
|
75
94
|
searchState.hasResults &&
|
|
76
95
|
index === selectedIndex
|
|
77
|
-
?
|
|
96
|
+
? isSelected
|
|
97
|
+
? ' (enter to run)'
|
|
98
|
+
: colors.dim(' (enter to run)')
|
|
78
99
|
: '';
|
|
79
|
-
|
|
100
|
+
const row = `${pointer}${numberLabel}. ${option.entry.emoji} ${label} ${isSelected ? option.entry.metaLabel : colors.dim(option.entry.metaLabel)}${runHint}`;
|
|
101
|
+
lines.push(isSelected ? highlightHoveredLine(row) : row);
|
|
80
102
|
return;
|
|
81
103
|
}
|
|
82
104
|
const icon = option.action === 'back'
|
|
@@ -89,12 +111,9 @@ function formatInteractiveLines(state, selectedIndex, title, searchState) {
|
|
|
89
111
|
: option.action === 'next'
|
|
90
112
|
? 'Next page'
|
|
91
113
|
: 'Back';
|
|
92
|
-
const navLabel =
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
? baseLabel
|
|
96
|
-
: colors.dim(baseLabel);
|
|
97
|
-
lines.push(`${pointer}${numberLabel}. ${icon} ${navLabel}`);
|
|
114
|
+
const navLabel = option.enabled ? baseLabel : colors.dim(baseLabel);
|
|
115
|
+
const row = `${pointer}${numberLabel}. ${icon} ${navLabel}`;
|
|
116
|
+
lines.push(isSelected ? highlightHoveredLine(row) : row);
|
|
98
117
|
});
|
|
99
118
|
if (searchState?.active && !searchState.hasResults) {
|
|
100
119
|
lines.push(colors.yellow('No scripts match your search.'));
|
|
@@ -111,16 +130,6 @@ function formatInteractiveLines(state, selectedIndex, title, searchState) {
|
|
|
111
130
|
}
|
|
112
131
|
function renderInteractiveList(lines, previousLineCount) {
|
|
113
132
|
const columns = process.stdout.columns ?? 80;
|
|
114
|
-
const ansiRegex = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
|
|
115
|
-
const visibleLength = (text) => {
|
|
116
|
-
const clean = text.replace(ansiRegex, '');
|
|
117
|
-
let length = 0;
|
|
118
|
-
for (const ch of clean) {
|
|
119
|
-
const code = ch.codePointAt(0) ?? 0;
|
|
120
|
-
length += code > 0xffff ? 2 : 1;
|
|
121
|
-
}
|
|
122
|
-
return length;
|
|
123
|
-
};
|
|
124
133
|
const nextLineCount = lines.reduce((total, line) => {
|
|
125
134
|
const len = visibleLength(line);
|
|
126
135
|
const wrapped = Math.max(1, Math.ceil(len / columns));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emeryld/manager",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.4",
|
|
4
4
|
"description": "Interactive manager for pnpm monorepos (update/test/build/publish).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"typescript": "^5.9.3"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@types/node": "^20.19.
|
|
32
|
+
"@types/node": "^20.19.37",
|
|
33
33
|
"@types/semver": "^7.7.1",
|
|
34
34
|
"cross-env": "^7.0.3"
|
|
35
35
|
},
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"manager-cli": "node dist/manager-cli.js",
|
|
44
|
+
"update:agent-context": "node tools/update-agent-context.js",
|
|
44
45
|
"build": "tsc -p tsconfig.base.json && node scripts/copy-manager-cli.mjs",
|
|
45
46
|
"typecheck": "tsc -p tsconfig.base.json --noEmit",
|
|
46
47
|
"test": "cross-env TS_NODE_PROJECT=tsconfig.test.json node --loader ts-node/esm --test test/*.test.ts"
|