@gxp-dev/tools 2.0.62 → 2.0.64
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 +32 -31
- package/bin/gx-devtools.js +74 -54
- package/bin/lib/cli.js +23 -21
- package/bin/lib/commands/add-dependency.js +366 -325
- package/bin/lib/commands/assets.js +137 -139
- package/bin/lib/commands/build.js +169 -174
- package/bin/lib/commands/datastore.js +181 -183
- package/bin/lib/commands/dev.js +127 -131
- package/bin/lib/commands/extensions.js +147 -149
- package/bin/lib/commands/extract-config.js +73 -67
- package/bin/lib/commands/index.js +12 -12
- package/bin/lib/commands/init.js +342 -240
- package/bin/lib/commands/publish.js +69 -75
- package/bin/lib/commands/socket.js +69 -69
- package/bin/lib/commands/ssl.js +14 -14
- package/bin/lib/constants.js +10 -24
- package/bin/lib/tui/App.tsx +761 -705
- package/bin/lib/tui/components/AIPanel.tsx +191 -171
- package/bin/lib/tui/components/CommandInput.tsx +394 -343
- package/bin/lib/tui/components/GeminiPanel.tsx +175 -151
- package/bin/lib/tui/components/Header.tsx +23 -21
- package/bin/lib/tui/components/LogPanel.tsx +244 -220
- package/bin/lib/tui/components/TabBar.tsx +50 -48
- package/bin/lib/tui/components/WelcomeScreen.tsx +126 -71
- package/bin/lib/tui/index.tsx +37 -39
- package/bin/lib/tui/services/AIService.ts +518 -462
- package/bin/lib/tui/services/ExtensionService.ts +140 -129
- package/bin/lib/tui/services/GeminiService.ts +367 -337
- package/bin/lib/tui/services/ServiceManager.ts +344 -322
- package/bin/lib/tui/services/SocketService.ts +168 -168
- package/bin/lib/tui/services/ViteService.ts +88 -88
- package/bin/lib/tui/services/index.ts +47 -22
- package/bin/lib/utils/ai-scaffold.js +291 -280
- package/bin/lib/utils/extract-config.js +157 -140
- package/bin/lib/utils/files.js +82 -86
- package/bin/lib/utils/index.js +7 -7
- package/bin/lib/utils/paths.js +34 -34
- package/bin/lib/utils/prompts.js +194 -169
- package/bin/lib/utils/ssl.js +79 -81
- package/browser-extensions/README.md +0 -1
- package/browser-extensions/chrome/background.js +244 -237
- package/browser-extensions/chrome/content.js +32 -29
- package/browser-extensions/chrome/devtools.html +7 -7
- package/browser-extensions/chrome/devtools.js +19 -19
- package/browser-extensions/chrome/inspector.js +802 -767
- package/browser-extensions/chrome/manifest.json +71 -63
- package/browser-extensions/chrome/panel.html +674 -636
- package/browser-extensions/chrome/panel.js +722 -712
- package/browser-extensions/chrome/popup.html +586 -543
- package/browser-extensions/chrome/popup.js +282 -244
- package/browser-extensions/chrome/rules.json +1 -1
- package/browser-extensions/chrome/test-chrome.html +216 -136
- package/browser-extensions/chrome/test-mixed-content.html +284 -189
- package/browser-extensions/chrome/test-uri-pattern.html +221 -198
- package/browser-extensions/firefox/README.md +9 -6
- package/browser-extensions/firefox/background.js +221 -218
- package/browser-extensions/firefox/content.js +55 -52
- package/browser-extensions/firefox/debug-errors.html +386 -228
- package/browser-extensions/firefox/debug-https.html +153 -105
- package/browser-extensions/firefox/devtools.html +7 -7
- package/browser-extensions/firefox/devtools.js +23 -20
- package/browser-extensions/firefox/inspector.js +802 -767
- package/browser-extensions/firefox/manifest.json +68 -68
- package/browser-extensions/firefox/panel.html +674 -636
- package/browser-extensions/firefox/panel.js +722 -712
- package/browser-extensions/firefox/popup.html +572 -535
- package/browser-extensions/firefox/popup.js +281 -236
- package/browser-extensions/firefox/test-gramercy.html +170 -125
- package/browser-extensions/firefox/test-imports.html +59 -55
- package/browser-extensions/firefox/test-masking.html +231 -140
- package/browser-extensions/firefox/test-uri-pattern.html +221 -198
- package/dist/tui/App.d.ts +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +154 -150
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/components/AIPanel.d.ts.map +1 -1
- package/dist/tui/components/AIPanel.js +42 -35
- package/dist/tui/components/AIPanel.js.map +1 -1
- package/dist/tui/components/CommandInput.d.ts +1 -1
- package/dist/tui/components/CommandInput.d.ts.map +1 -1
- package/dist/tui/components/CommandInput.js +92 -62
- package/dist/tui/components/CommandInput.js.map +1 -1
- package/dist/tui/components/GeminiPanel.d.ts.map +1 -1
- package/dist/tui/components/GeminiPanel.js +37 -30
- package/dist/tui/components/GeminiPanel.js.map +1 -1
- package/dist/tui/components/Header.d.ts.map +1 -1
- package/dist/tui/components/Header.js +1 -1
- package/dist/tui/components/Header.js.map +1 -1
- package/dist/tui/components/LogPanel.d.ts +1 -1
- package/dist/tui/components/LogPanel.d.ts.map +1 -1
- package/dist/tui/components/LogPanel.js +26 -24
- package/dist/tui/components/LogPanel.js.map +1 -1
- package/dist/tui/components/TabBar.d.ts +2 -2
- package/dist/tui/components/TabBar.d.ts.map +1 -1
- package/dist/tui/components/TabBar.js +11 -11
- package/dist/tui/components/TabBar.js.map +1 -1
- package/dist/tui/components/WelcomeScreen.d.ts.map +1 -1
- package/dist/tui/components/WelcomeScreen.js +6 -6
- package/dist/tui/components/WelcomeScreen.js.map +1 -1
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +8 -8
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/services/AIService.d.ts +2 -2
- package/dist/tui/services/AIService.d.ts.map +1 -1
- package/dist/tui/services/AIService.js +165 -125
- package/dist/tui/services/AIService.js.map +1 -1
- package/dist/tui/services/ExtensionService.d.ts +1 -1
- package/dist/tui/services/ExtensionService.d.ts.map +1 -1
- package/dist/tui/services/ExtensionService.js +33 -26
- package/dist/tui/services/ExtensionService.js.map +1 -1
- package/dist/tui/services/GeminiService.d.ts +1 -1
- package/dist/tui/services/GeminiService.d.ts.map +1 -1
- package/dist/tui/services/GeminiService.js +87 -76
- package/dist/tui/services/GeminiService.js.map +1 -1
- package/dist/tui/services/ServiceManager.d.ts +3 -3
- package/dist/tui/services/ServiceManager.d.ts.map +1 -1
- package/dist/tui/services/ServiceManager.js +72 -58
- package/dist/tui/services/ServiceManager.js.map +1 -1
- package/dist/tui/services/SocketService.d.ts.map +1 -1
- package/dist/tui/services/SocketService.js +32 -32
- package/dist/tui/services/SocketService.js.map +1 -1
- package/dist/tui/services/ViteService.d.ts.map +1 -1
- package/dist/tui/services/ViteService.js +26 -28
- package/dist/tui/services/ViteService.js.map +1 -1
- package/dist/tui/services/index.d.ts +6 -6
- package/dist/tui/services/index.d.ts.map +1 -1
- package/dist/tui/services/index.js +6 -6
- package/dist/tui/services/index.js.map +1 -1
- package/mcp/gxp-api-server.js +83 -81
- package/package.json +109 -93
- package/runtime/PortalContainer.vue +258 -234
- package/runtime/dev-tools/DevToolsModal.vue +153 -155
- package/runtime/dev-tools/LayoutSwitcher.vue +144 -140
- package/runtime/dev-tools/MockDataEditor.vue +456 -433
- package/runtime/dev-tools/SocketSimulator.vue +379 -371
- package/runtime/dev-tools/StoreInspector.vue +517 -455
- package/runtime/dev-tools/index.js +5 -5
- package/runtime/fallback-layouts/PrivateLayout.vue +2 -2
- package/runtime/fallback-layouts/PublicLayout.vue +2 -2
- package/runtime/fallback-layouts/SystemLayout.vue +2 -2
- package/runtime/gxpStringsPlugin.js +159 -134
- package/runtime/index.html +17 -19
- package/runtime/main.js +24 -22
- package/runtime/mock-api/auth-middleware.js +15 -15
- package/runtime/mock-api/image-generator.js +46 -46
- package/runtime/mock-api/index.js +55 -55
- package/runtime/mock-api/response-generator.js +116 -105
- package/runtime/mock-api/route-generator.js +107 -84
- package/runtime/mock-api/socket-triggers.js +94 -93
- package/runtime/mock-api/spec-loader.js +79 -80
- package/runtime/package.json +3 -0
- package/runtime/server.js +68 -68
- package/runtime/stores/gxpPortalConfigStore.js +204 -186
- package/runtime/stores/index.js +2 -2
- package/runtime/vite-inspector-plugin.js +858 -707
- package/runtime/vite-source-tracker-plugin.js +132 -113
- package/runtime/vite.config.js +207 -132
- package/scripts/launch-chrome.js +41 -41
- package/scripts/pack-chrome.js +38 -39
- package/socket-events/AiSessionMessageCreated.json +17 -17
- package/socket-events/SocialStreamPostCreated.json +23 -23
- package/socket-events/SocialStreamPostVariantCompleted.json +22 -22
- package/template/.claude/agents/gxp-developer.md +100 -99
- package/template/.claude/settings.json +7 -7
- package/template/AGENTS.md +30 -23
- package/template/GEMINI.md +20 -20
- package/template/README.md +70 -53
- package/template/app-manifest.json +2 -4
- package/template/configuration.json +10 -10
- package/template/default-styling.css +1 -1
- package/template/index.html +18 -20
- package/template/main.js +24 -22
- package/template/src/DemoPage.vue +415 -362
- package/template/src/Plugin.vue +76 -85
- package/template/src/stores/index.js +3 -3
- package/template/src/stores/test-data.json +164 -172
- package/template/theme-layouts/AdditionalStyling.css +50 -50
- package/template/theme-layouts/PrivateLayout.vue +8 -12
- package/template/theme-layouts/PublicLayout.vue +8 -12
- package/template/theme-layouts/SystemLayout.vue +8 -12
- package/template/vite.extend.js +45 -0
- package/template/vite.config.js +0 -409
package/bin/lib/utils/prompts.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Handles interactive CLI prompts.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const readline = require("readline")
|
|
7
|
+
const readline = require("readline")
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Prompts user for input
|
|
@@ -15,14 +15,14 @@ function promptUser(question) {
|
|
|
15
15
|
const rl = readline.createInterface({
|
|
16
16
|
input: process.stdin,
|
|
17
17
|
output: process.stdout,
|
|
18
|
-
})
|
|
18
|
+
})
|
|
19
19
|
|
|
20
20
|
return new Promise((resolve) => {
|
|
21
21
|
rl.question(question, (answer) => {
|
|
22
|
-
rl.close()
|
|
23
|
-
resolve(answer)
|
|
24
|
-
})
|
|
25
|
-
})
|
|
22
|
+
rl.close()
|
|
23
|
+
resolve(answer)
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/**
|
|
@@ -32,14 +32,14 @@ function promptUser(question) {
|
|
|
32
32
|
* @returns {Promise<boolean>} True if user confirmed
|
|
33
33
|
*/
|
|
34
34
|
async function confirmPrompt(question, defaultYes = true) {
|
|
35
|
-
const suffix = defaultYes ? "(Y/n)" : "(y/N)"
|
|
36
|
-
const answer = await promptUser(`${question} ${suffix}: `)
|
|
35
|
+
const suffix = defaultYes ? "(Y/n)" : "(y/N)"
|
|
36
|
+
const answer = await promptUser(`${question} ${suffix}: `)
|
|
37
37
|
|
|
38
38
|
if (answer === "") {
|
|
39
|
-
return defaultYes
|
|
39
|
+
return defaultYes
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes"
|
|
42
|
+
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes"
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
@@ -49,19 +49,19 @@ async function confirmPrompt(question, defaultYes = true) {
|
|
|
49
49
|
* @returns {Promise<number>} Index of selected option
|
|
50
50
|
*/
|
|
51
51
|
async function selectPrompt(question, options) {
|
|
52
|
-
console.log(question)
|
|
52
|
+
console.log(question)
|
|
53
53
|
options.forEach((opt, i) => {
|
|
54
|
-
console.log(` ${i + 1}. ${opt}`)
|
|
55
|
-
})
|
|
54
|
+
console.log(` ${i + 1}. ${opt}`)
|
|
55
|
+
})
|
|
56
56
|
|
|
57
|
-
const answer = await promptUser("Enter number: ")
|
|
58
|
-
const index = parseInt(answer, 10) - 1
|
|
57
|
+
const answer = await promptUser("Enter number: ")
|
|
58
|
+
const index = parseInt(answer, 10) - 1
|
|
59
59
|
|
|
60
60
|
if (index >= 0 && index < options.length) {
|
|
61
|
-
return index
|
|
61
|
+
return index
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
return 0
|
|
64
|
+
return 0 // Default to first option
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/**
|
|
@@ -70,35 +70,38 @@ async function selectPrompt(question, options) {
|
|
|
70
70
|
* @param {string} hint - Hint text shown below prompt
|
|
71
71
|
* @returns {Promise<string>} The multi-line input
|
|
72
72
|
*/
|
|
73
|
-
async function multiLinePrompt(
|
|
74
|
-
|
|
73
|
+
async function multiLinePrompt(
|
|
74
|
+
question,
|
|
75
|
+
hint = "Enter an empty line to finish",
|
|
76
|
+
) {
|
|
77
|
+
const readline = require("readline")
|
|
75
78
|
|
|
76
|
-
console.log(question)
|
|
77
|
-
console.log(` (${hint})`)
|
|
78
|
-
console.log("")
|
|
79
|
+
console.log(question)
|
|
80
|
+
console.log(` (${hint})`)
|
|
81
|
+
console.log("")
|
|
79
82
|
|
|
80
83
|
const rl = readline.createInterface({
|
|
81
84
|
input: process.stdin,
|
|
82
85
|
output: process.stdout,
|
|
83
|
-
})
|
|
86
|
+
})
|
|
84
87
|
|
|
85
88
|
return new Promise((resolve) => {
|
|
86
|
-
const lines = []
|
|
89
|
+
const lines = []
|
|
87
90
|
|
|
88
91
|
const promptLine = () => {
|
|
89
92
|
rl.question(" > ", (line) => {
|
|
90
93
|
if (line === "") {
|
|
91
|
-
rl.close()
|
|
92
|
-
resolve(lines.join("\n").trim())
|
|
94
|
+
rl.close()
|
|
95
|
+
resolve(lines.join("\n").trim())
|
|
93
96
|
} else {
|
|
94
|
-
lines.push(line)
|
|
95
|
-
promptLine()
|
|
97
|
+
lines.push(line)
|
|
98
|
+
promptLine()
|
|
96
99
|
}
|
|
97
|
-
})
|
|
98
|
-
}
|
|
100
|
+
})
|
|
101
|
+
}
|
|
99
102
|
|
|
100
|
-
promptLine()
|
|
101
|
-
})
|
|
103
|
+
promptLine()
|
|
104
|
+
})
|
|
102
105
|
}
|
|
103
106
|
|
|
104
107
|
/**
|
|
@@ -108,9 +111,9 @@ async function multiLinePrompt(question, hint = "Enter an empty line to finish")
|
|
|
108
111
|
* @returns {Promise<string>} The user's answer or default
|
|
109
112
|
*/
|
|
110
113
|
async function promptWithDefault(question, defaultValue = "") {
|
|
111
|
-
const suffix = defaultValue ? ` [${defaultValue}]` : ""
|
|
112
|
-
const answer = await promptUser(`${question}${suffix}: `)
|
|
113
|
-
return answer || defaultValue
|
|
114
|
+
const suffix = defaultValue ? ` [${defaultValue}]` : ""
|
|
115
|
+
const answer = await promptUser(`${question}${suffix}: `)
|
|
116
|
+
return answer || defaultValue
|
|
114
117
|
}
|
|
115
118
|
|
|
116
119
|
/**
|
|
@@ -122,179 +125,193 @@ async function promptWithDefault(question, defaultValue = "") {
|
|
|
122
125
|
*/
|
|
123
126
|
async function arrowSelectPrompt(question, options, defaultIndex = 0) {
|
|
124
127
|
return new Promise((resolve) => {
|
|
125
|
-
const stdin = process.stdin
|
|
126
|
-
const stdout = process.stdout
|
|
128
|
+
const stdin = process.stdin
|
|
129
|
+
const stdout = process.stdout
|
|
127
130
|
|
|
128
|
-
let selectedIndex = defaultIndex
|
|
129
|
-
let customInput = ""
|
|
130
|
-
let isCustomMode = false
|
|
131
|
+
let selectedIndex = defaultIndex
|
|
132
|
+
let customInput = ""
|
|
133
|
+
let isCustomMode = false
|
|
131
134
|
|
|
132
135
|
// Check if last option is custom input
|
|
133
136
|
const hasCustomOption = options.some(
|
|
134
|
-
(opt) => opt.value === "__custom__" || opt.isCustomInput
|
|
135
|
-
)
|
|
137
|
+
(opt) => opt.value === "__custom__" || opt.isCustomInput,
|
|
138
|
+
)
|
|
136
139
|
|
|
137
140
|
const render = () => {
|
|
138
141
|
// Clear previous render
|
|
139
|
-
stdout.write("\x1B[?25l")
|
|
142
|
+
stdout.write("\x1B[?25l") // Hide cursor
|
|
140
143
|
|
|
141
144
|
// Move cursor up to overwrite previous options
|
|
142
145
|
if (options.length > 0) {
|
|
143
|
-
stdout.write(`\x1B[${options.length + 1}A`)
|
|
146
|
+
stdout.write(`\x1B[${options.length + 1}A`) // +1 for question line
|
|
144
147
|
}
|
|
145
148
|
|
|
146
149
|
// Clear lines
|
|
147
150
|
for (let i = 0; i <= options.length; i++) {
|
|
148
|
-
stdout.write("\x1B[2K\n")
|
|
151
|
+
stdout.write("\x1B[2K\n")
|
|
149
152
|
}
|
|
150
|
-
stdout.write(`\x1B[${options.length + 1}A`)
|
|
153
|
+
stdout.write(`\x1B[${options.length + 1}A`)
|
|
151
154
|
|
|
152
155
|
// Print question
|
|
153
|
-
stdout.write(`\x1B[36m?\x1B[0m ${question}\n`)
|
|
156
|
+
stdout.write(`\x1B[36m?\x1B[0m ${question}\n`)
|
|
154
157
|
|
|
155
158
|
// Print options
|
|
156
159
|
options.forEach((opt, i) => {
|
|
157
|
-
const isSelected = i === selectedIndex
|
|
158
|
-
const prefix = isSelected ? "\x1B[36m❯\x1B[0m" : " "
|
|
159
|
-
const label = isSelected ? `\x1B[36m${opt.label}\x1B[0m` : opt.label
|
|
160
|
+
const isSelected = i === selectedIndex
|
|
161
|
+
const prefix = isSelected ? "\x1B[36m❯\x1B[0m" : " "
|
|
162
|
+
const label = isSelected ? `\x1B[36m${opt.label}\x1B[0m` : opt.label
|
|
160
163
|
|
|
161
164
|
if (opt.isCustomInput && isSelected && isCustomMode) {
|
|
162
|
-
stdout.write(`${prefix} ${opt.label}: ${customInput}█\n`)
|
|
165
|
+
stdout.write(`${prefix} ${opt.label}: ${customInput}█\n`)
|
|
163
166
|
} else if (opt.description && !opt.isCustomInput) {
|
|
164
|
-
stdout.write(
|
|
167
|
+
stdout.write(
|
|
168
|
+
`${prefix} ${label} \x1B[90m- ${opt.description}\x1B[0m\n`,
|
|
169
|
+
)
|
|
165
170
|
} else {
|
|
166
|
-
stdout.write(`${prefix} ${label}\n`)
|
|
171
|
+
stdout.write(`${prefix} ${label}\n`)
|
|
167
172
|
}
|
|
168
|
-
})
|
|
173
|
+
})
|
|
169
174
|
|
|
170
|
-
stdout.write("\x1B[?25h")
|
|
171
|
-
}
|
|
175
|
+
stdout.write("\x1B[?25h") // Show cursor
|
|
176
|
+
}
|
|
172
177
|
|
|
173
178
|
const cleanup = () => {
|
|
174
|
-
stdin.setRawMode(false)
|
|
175
|
-
stdin.removeAllListeners("data")
|
|
176
|
-
stdin.pause()
|
|
179
|
+
stdin.setRawMode(false)
|
|
180
|
+
stdin.removeAllListeners("data")
|
|
181
|
+
stdin.pause()
|
|
177
182
|
// Clear the selection UI
|
|
178
|
-
stdout.write(`\x1B[${options.length + 1}A`)
|
|
183
|
+
stdout.write(`\x1B[${options.length + 1}A`)
|
|
179
184
|
for (let i = 0; i <= options.length; i++) {
|
|
180
|
-
stdout.write("\x1B[2K\n")
|
|
185
|
+
stdout.write("\x1B[2K\n")
|
|
181
186
|
}
|
|
182
|
-
stdout.write(`\x1B[${options.length + 1}A`)
|
|
183
|
-
}
|
|
187
|
+
stdout.write(`\x1B[${options.length + 1}A`)
|
|
188
|
+
}
|
|
184
189
|
|
|
185
190
|
const handleKey = (key) => {
|
|
186
|
-
const currentOption = options[selectedIndex]
|
|
191
|
+
const currentOption = options[selectedIndex]
|
|
187
192
|
|
|
188
193
|
// Handle Ctrl+C
|
|
189
194
|
if (key === "\x03") {
|
|
190
|
-
cleanup()
|
|
191
|
-
process.exit(0)
|
|
195
|
+
cleanup()
|
|
196
|
+
process.exit(0)
|
|
192
197
|
}
|
|
193
198
|
|
|
194
199
|
// Handle Enter
|
|
195
200
|
if (key === "\r" || key === "\n") {
|
|
196
|
-
cleanup()
|
|
201
|
+
cleanup()
|
|
197
202
|
|
|
198
203
|
if (currentOption.isCustomInput) {
|
|
199
204
|
// Print the final selection
|
|
200
205
|
stdout.write(
|
|
201
|
-
`\x1B[36m?\x1B[0m ${question} \x1B[36m${customInput || currentOption.defaultValue || ""}\x1B[0m\n
|
|
202
|
-
)
|
|
203
|
-
resolve(customInput || currentOption.defaultValue || "")
|
|
206
|
+
`\x1B[36m?\x1B[0m ${question} \x1B[36m${customInput || currentOption.defaultValue || ""}\x1B[0m\n`,
|
|
207
|
+
)
|
|
208
|
+
resolve(customInput || currentOption.defaultValue || "")
|
|
204
209
|
} else {
|
|
205
210
|
// Print the final selection
|
|
206
211
|
stdout.write(
|
|
207
|
-
`\x1B[36m?\x1B[0m ${question} \x1B[36m${currentOption.label}\x1B[0m\n
|
|
208
|
-
)
|
|
209
|
-
resolve(currentOption.value)
|
|
212
|
+
`\x1B[36m?\x1B[0m ${question} \x1B[36m${currentOption.label}\x1B[0m\n`,
|
|
213
|
+
)
|
|
214
|
+
resolve(currentOption.value)
|
|
210
215
|
}
|
|
211
|
-
return
|
|
216
|
+
return
|
|
212
217
|
}
|
|
213
218
|
|
|
214
219
|
// In custom input mode, handle typing
|
|
215
220
|
if (currentOption && currentOption.isCustomInput && isCustomMode) {
|
|
216
221
|
// Backspace
|
|
217
222
|
if (key === "\x7f" || key === "\b") {
|
|
218
|
-
customInput = customInput.slice(0, -1)
|
|
219
|
-
render()
|
|
220
|
-
return
|
|
223
|
+
customInput = customInput.slice(0, -1)
|
|
224
|
+
render()
|
|
225
|
+
return
|
|
221
226
|
}
|
|
222
227
|
|
|
223
228
|
// Escape - exit custom mode
|
|
224
229
|
if (key === "\x1b" && key.length === 1) {
|
|
225
|
-
isCustomMode = false
|
|
226
|
-
render()
|
|
227
|
-
return
|
|
230
|
+
isCustomMode = false
|
|
231
|
+
render()
|
|
232
|
+
return
|
|
228
233
|
}
|
|
229
234
|
|
|
230
235
|
// Regular character input (printable ASCII)
|
|
231
|
-
if (
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
236
|
+
if (
|
|
237
|
+
key.length === 1 &&
|
|
238
|
+
key.charCodeAt(0) >= 32 &&
|
|
239
|
+
key.charCodeAt(0) < 127
|
|
240
|
+
) {
|
|
241
|
+
customInput += key
|
|
242
|
+
render()
|
|
243
|
+
return
|
|
235
244
|
}
|
|
236
245
|
}
|
|
237
246
|
|
|
238
247
|
// Arrow keys (escape sequences)
|
|
239
248
|
if (key === "\x1b[A" || key === "\x1bOA") {
|
|
240
249
|
// Up arrow
|
|
241
|
-
selectedIndex = Math.max(0, selectedIndex - 1)
|
|
242
|
-
isCustomMode = options[selectedIndex]?.isCustomInput || false
|
|
243
|
-
render()
|
|
250
|
+
selectedIndex = Math.max(0, selectedIndex - 1)
|
|
251
|
+
isCustomMode = options[selectedIndex]?.isCustomInput || false
|
|
252
|
+
render()
|
|
244
253
|
} else if (key === "\x1b[B" || key === "\x1bOB") {
|
|
245
254
|
// Down arrow
|
|
246
|
-
selectedIndex = Math.min(options.length - 1, selectedIndex + 1)
|
|
247
|
-
isCustomMode = options[selectedIndex]?.isCustomInput || false
|
|
248
|
-
render()
|
|
249
|
-
} else if (
|
|
255
|
+
selectedIndex = Math.min(options.length - 1, selectedIndex + 1)
|
|
256
|
+
isCustomMode = options[selectedIndex]?.isCustomInput || false
|
|
257
|
+
render()
|
|
258
|
+
} else if (
|
|
259
|
+
currentOption &&
|
|
260
|
+
currentOption.isCustomInput &&
|
|
261
|
+
!isCustomMode
|
|
262
|
+
) {
|
|
250
263
|
// Start custom input mode on any printable character
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
264
|
+
if (
|
|
265
|
+
key.length === 1 &&
|
|
266
|
+
key.charCodeAt(0) >= 32 &&
|
|
267
|
+
key.charCodeAt(0) < 127
|
|
268
|
+
) {
|
|
269
|
+
isCustomMode = true
|
|
270
|
+
customInput = key
|
|
271
|
+
render()
|
|
255
272
|
}
|
|
256
273
|
}
|
|
257
|
-
}
|
|
274
|
+
}
|
|
258
275
|
|
|
259
276
|
// Initial render with spacing
|
|
260
|
-
console.log("")
|
|
261
|
-
stdout.write(`\x1B[36m?\x1B[0m ${question}\n`)
|
|
262
|
-
options.forEach(() => console.log(""))
|
|
277
|
+
console.log("") // Add space before question
|
|
278
|
+
stdout.write(`\x1B[36m?\x1B[0m ${question}\n`)
|
|
279
|
+
options.forEach(() => console.log(""))
|
|
263
280
|
|
|
264
|
-
stdin.setRawMode(true)
|
|
265
|
-
stdin.resume()
|
|
266
|
-
stdin.setEncoding("utf8")
|
|
281
|
+
stdin.setRawMode(true)
|
|
282
|
+
stdin.resume()
|
|
283
|
+
stdin.setEncoding("utf8")
|
|
267
284
|
|
|
268
|
-
let buffer = ""
|
|
285
|
+
let buffer = ""
|
|
269
286
|
stdin.on("data", (data) => {
|
|
270
|
-
buffer += data
|
|
287
|
+
buffer += data
|
|
271
288
|
|
|
272
289
|
// Process escape sequences
|
|
273
290
|
while (buffer.length > 0) {
|
|
274
291
|
// Check for escape sequences
|
|
275
292
|
if (buffer.startsWith("\x1b[") || buffer.startsWith("\x1bO")) {
|
|
276
293
|
if (buffer.length >= 3) {
|
|
277
|
-
handleKey(buffer.slice(0, 3))
|
|
278
|
-
buffer = buffer.slice(3)
|
|
294
|
+
handleKey(buffer.slice(0, 3))
|
|
295
|
+
buffer = buffer.slice(3)
|
|
279
296
|
} else {
|
|
280
|
-
break
|
|
297
|
+
break // Wait for more data
|
|
281
298
|
}
|
|
282
299
|
} else if (buffer.startsWith("\x1b")) {
|
|
283
300
|
if (buffer.length >= 2) {
|
|
284
|
-
handleKey(buffer.slice(0, 1))
|
|
285
|
-
buffer = buffer.slice(1)
|
|
301
|
+
handleKey(buffer.slice(0, 1))
|
|
302
|
+
buffer = buffer.slice(1)
|
|
286
303
|
} else {
|
|
287
|
-
break
|
|
304
|
+
break // Wait for more data
|
|
288
305
|
}
|
|
289
306
|
} else {
|
|
290
|
-
handleKey(buffer[0])
|
|
291
|
-
buffer = buffer.slice(1)
|
|
307
|
+
handleKey(buffer[0])
|
|
308
|
+
buffer = buffer.slice(1)
|
|
292
309
|
}
|
|
293
310
|
}
|
|
294
|
-
})
|
|
311
|
+
})
|
|
295
312
|
|
|
296
|
-
render()
|
|
297
|
-
})
|
|
313
|
+
render()
|
|
314
|
+
})
|
|
298
315
|
}
|
|
299
316
|
|
|
300
317
|
/**
|
|
@@ -307,109 +324,117 @@ async function arrowSelectPrompt(question, options, defaultIndex = 0) {
|
|
|
307
324
|
*/
|
|
308
325
|
async function inputWithDefault(question, defaultValue = "", placeholder = "") {
|
|
309
326
|
return new Promise((resolve) => {
|
|
310
|
-
const stdin = process.stdin
|
|
311
|
-
const stdout = process.stdout
|
|
327
|
+
const stdin = process.stdin
|
|
328
|
+
const stdout = process.stdout
|
|
312
329
|
|
|
313
|
-
let value = defaultValue
|
|
314
|
-
let cursorPos = value.length
|
|
330
|
+
let value = defaultValue
|
|
331
|
+
let cursorPos = value.length
|
|
315
332
|
|
|
316
333
|
const render = () => {
|
|
317
|
-
stdout.write("\x1B[?25l")
|
|
318
|
-
stdout.write("\r\x1B[2K")
|
|
334
|
+
stdout.write("\x1B[?25l") // Hide cursor
|
|
335
|
+
stdout.write("\r\x1B[2K") // Clear line
|
|
319
336
|
|
|
320
|
-
const displayValue = value || `\x1B[90m${placeholder}\x1B[0m
|
|
321
|
-
stdout.write(
|
|
337
|
+
const displayValue = value || `\x1B[90m${placeholder}\x1B[0m`
|
|
338
|
+
stdout.write(
|
|
339
|
+
`\x1B[36m?\x1B[0m ${question}: ${value ? value : displayValue}`,
|
|
340
|
+
)
|
|
322
341
|
|
|
323
342
|
// Position cursor
|
|
324
|
-
const totalLength = question.length + 4 + cursorPos
|
|
325
|
-
stdout.write(`\r\x1B[${totalLength}C`)
|
|
326
|
-
stdout.write("\x1B[?25h")
|
|
327
|
-
}
|
|
343
|
+
const totalLength = question.length + 4 + cursorPos // 4 = "? " + ": "
|
|
344
|
+
stdout.write(`\r\x1B[${totalLength}C`)
|
|
345
|
+
stdout.write("\x1B[?25h") // Show cursor
|
|
346
|
+
}
|
|
328
347
|
|
|
329
348
|
const cleanup = () => {
|
|
330
|
-
stdin.setRawMode(false)
|
|
331
|
-
stdin.removeAllListeners("data")
|
|
332
|
-
stdin.pause()
|
|
333
|
-
}
|
|
349
|
+
stdin.setRawMode(false)
|
|
350
|
+
stdin.removeAllListeners("data")
|
|
351
|
+
stdin.pause()
|
|
352
|
+
}
|
|
334
353
|
|
|
335
|
-
console.log("")
|
|
336
|
-
render()
|
|
354
|
+
console.log("") // Add space
|
|
355
|
+
render()
|
|
337
356
|
|
|
338
|
-
stdin.setRawMode(true)
|
|
339
|
-
stdin.resume()
|
|
340
|
-
stdin.setEncoding("utf8")
|
|
357
|
+
stdin.setRawMode(true)
|
|
358
|
+
stdin.resume()
|
|
359
|
+
stdin.setEncoding("utf8")
|
|
341
360
|
|
|
342
361
|
stdin.on("data", (key) => {
|
|
343
362
|
// Ctrl+C
|
|
344
363
|
if (key === "\x03") {
|
|
345
|
-
cleanup()
|
|
346
|
-
process.exit(0)
|
|
364
|
+
cleanup()
|
|
365
|
+
process.exit(0)
|
|
347
366
|
}
|
|
348
367
|
|
|
349
368
|
// Enter
|
|
350
369
|
if (key === "\r" || key === "\n") {
|
|
351
|
-
cleanup()
|
|
352
|
-
stdout.write("\r\x1B[2K")
|
|
353
|
-
stdout.write(
|
|
354
|
-
|
|
355
|
-
|
|
370
|
+
cleanup()
|
|
371
|
+
stdout.write("\r\x1B[2K")
|
|
372
|
+
stdout.write(
|
|
373
|
+
`\x1B[36m?\x1B[0m ${question}: \x1B[36m${value || defaultValue}\x1B[0m\n`,
|
|
374
|
+
)
|
|
375
|
+
resolve(value || defaultValue)
|
|
376
|
+
return
|
|
356
377
|
}
|
|
357
378
|
|
|
358
379
|
// Backspace
|
|
359
380
|
if (key === "\x7f" || key === "\b") {
|
|
360
381
|
if (cursorPos > 0) {
|
|
361
|
-
value = value.slice(0, cursorPos - 1) + value.slice(cursorPos)
|
|
362
|
-
cursorPos
|
|
382
|
+
value = value.slice(0, cursorPos - 1) + value.slice(cursorPos)
|
|
383
|
+
cursorPos--
|
|
363
384
|
}
|
|
364
|
-
render()
|
|
365
|
-
return
|
|
385
|
+
render()
|
|
386
|
+
return
|
|
366
387
|
}
|
|
367
388
|
|
|
368
389
|
// Delete
|
|
369
390
|
if (key === "\x1b[3~") {
|
|
370
391
|
if (cursorPos < value.length) {
|
|
371
|
-
value = value.slice(0, cursorPos) + value.slice(cursorPos + 1)
|
|
392
|
+
value = value.slice(0, cursorPos) + value.slice(cursorPos + 1)
|
|
372
393
|
}
|
|
373
|
-
render()
|
|
374
|
-
return
|
|
394
|
+
render()
|
|
395
|
+
return
|
|
375
396
|
}
|
|
376
397
|
|
|
377
398
|
// Left arrow
|
|
378
399
|
if (key === "\x1b[D") {
|
|
379
|
-
cursorPos = Math.max(0, cursorPos - 1)
|
|
380
|
-
render()
|
|
381
|
-
return
|
|
400
|
+
cursorPos = Math.max(0, cursorPos - 1)
|
|
401
|
+
render()
|
|
402
|
+
return
|
|
382
403
|
}
|
|
383
404
|
|
|
384
405
|
// Right arrow
|
|
385
406
|
if (key === "\x1b[C") {
|
|
386
|
-
cursorPos = Math.min(value.length, cursorPos + 1)
|
|
387
|
-
render()
|
|
388
|
-
return
|
|
407
|
+
cursorPos = Math.min(value.length, cursorPos + 1)
|
|
408
|
+
render()
|
|
409
|
+
return
|
|
389
410
|
}
|
|
390
411
|
|
|
391
412
|
// Home
|
|
392
413
|
if (key === "\x1b[H" || key === "\x01") {
|
|
393
|
-
cursorPos = 0
|
|
394
|
-
render()
|
|
395
|
-
return
|
|
414
|
+
cursorPos = 0
|
|
415
|
+
render()
|
|
416
|
+
return
|
|
396
417
|
}
|
|
397
418
|
|
|
398
419
|
// End
|
|
399
420
|
if (key === "\x1b[F" || key === "\x05") {
|
|
400
|
-
cursorPos = value.length
|
|
401
|
-
render()
|
|
402
|
-
return
|
|
421
|
+
cursorPos = value.length
|
|
422
|
+
render()
|
|
423
|
+
return
|
|
403
424
|
}
|
|
404
425
|
|
|
405
426
|
// Regular character
|
|
406
|
-
if (
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
427
|
+
if (
|
|
428
|
+
key.length === 1 &&
|
|
429
|
+
key.charCodeAt(0) >= 32 &&
|
|
430
|
+
key.charCodeAt(0) < 127
|
|
431
|
+
) {
|
|
432
|
+
value = value.slice(0, cursorPos) + key + value.slice(cursorPos)
|
|
433
|
+
cursorPos++
|
|
434
|
+
render()
|
|
410
435
|
}
|
|
411
|
-
})
|
|
412
|
-
})
|
|
436
|
+
})
|
|
437
|
+
})
|
|
413
438
|
}
|
|
414
439
|
|
|
415
440
|
module.exports = {
|
|
@@ -420,4 +445,4 @@ module.exports = {
|
|
|
420
445
|
promptWithDefault,
|
|
421
446
|
arrowSelectPrompt,
|
|
422
447
|
inputWithDefault,
|
|
423
|
-
}
|
|
448
|
+
}
|