Skip to content

Commit ccc7dc8

Browse files
committed
fix: confirm before probes, no interactive prompts after raw mode
Submit flow: show detected terminal → confirm with Enter → run probes → submit. Confirmation happens before raw mode so stdin is clean. No readline prompts after probes (which left stdin in a broken state on iTerm2/Kitty). Override with --terminal-name and --terminal-version flags.
1 parent bd09a8f commit ccc7dc8

3 files changed

Lines changed: 44 additions & 59 deletions

File tree

cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "terminfo.dev",
3-
"version": "1.5.0",
3+
"version": "1.6.0",
44
"description": "Test your terminal's feature support and contribute to terminfo.dev",
55
"keywords": [
66
"ansi",

cli/src/index.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,21 +173,45 @@ program
173173
program
174174
.command("submit")
175175
.description("Run all probes and submit results to terminfo.dev via GitHub issue")
176-
.action(async () => {
176+
.option("--terminal-name <name>", "Override detected terminal name")
177+
.option("--terminal-version <version>", "Override detected terminal version")
178+
.action(async (opts) => {
179+
// Confirm details BEFORE probes (stdin is still clean)
180+
const terminal = detectTerminal()
181+
const name = opts.terminalName ?? terminal.name
182+
const version = opts.terminalVersion ?? terminal.version
183+
184+
printHeader(terminal)
185+
console.log(``)
186+
console.log(` Will submit results for \x1b[1m${name}${version ? ` ${version}` : ""}\x1b[0m on ${terminal.os}`)
187+
if (!version) {
188+
console.log(` \x1b[33m⚠ No version detected. Use --terminal-version to specify.\x1b[0m`)
189+
}
190+
191+
const { createInterface } = await import("node:readline")
192+
const rl = createInterface({ input: process.stdin, output: process.stdout })
193+
const proceed = await new Promise<string>((resolve) => {
194+
rl.question(`\n Press Enter to run probes and submit (or Ctrl+C to cancel) `, (answer) => {
195+
rl.close()
196+
resolve(answer)
197+
})
198+
})
199+
200+
// Now run probes (stdin goes to raw mode, no conflict)
177201
const data = await runProbes()
178202
printResults(data)
179203

180204
console.log(`\nSubmitting results to terminfo.dev...`)
181205
const url = await submitResults({
182-
terminal: data.terminal.name,
183-
terminalVersion: data.terminal.version,
206+
terminal: name,
207+
terminalVersion: version,
184208
os: data.terminal.os,
185209
osVersion: data.terminal.osVersion,
186210
results: data.results,
187211
notes: data.notes,
188212
responses: data.responses,
189213
generated: new Date().toISOString(),
190-
cliVersion: "1.5.0",
214+
cliVersion: "1.6.0",
191215
probeCount: ALL_PROBES.length,
192216
})
193217
if (url) {

cli/src/submit.ts

Lines changed: 15 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { writeFileSync, unlinkSync } from "node:fs"
66
import { tmpdir } from "node:os"
77
import { join } from "node:path"
88
import { execFileSync } from "node:child_process"
9-
import { createInterface } from "node:readline"
109

1110
const REPO = "beorn/terminfo.dev"
1211

@@ -23,65 +22,28 @@ interface SubmitData {
2322
probeCount?: number
2423
}
2524

26-
/** Drain any leftover bytes from stdin (e.g., late-arriving escape sequence responses) */
27-
async function drainStdin(): Promise<void> {
28-
return new Promise((resolve) => {
29-
if (!process.stdin.readable) { resolve(); return }
30-
process.stdin.resume()
31-
const timer = setTimeout(() => {
32-
process.stdin.pause()
33-
process.stdin.removeAllListeners("readable")
34-
resolve()
35-
}, 200)
36-
process.stdin.on("readable", () => {
37-
while (process.stdin.read() !== null) {} // discard
38-
})
39-
timer.unref()
40-
})
41-
}
42-
43-
async function prompt(question: string, defaultValue?: string): Promise<string> {
44-
await drainStdin()
45-
const rl = createInterface({ input: process.stdin, output: process.stdout })
46-
const suffix = defaultValue ? ` [${defaultValue}]` : ""
47-
return new Promise((resolve) => {
48-
rl.question(`${question}${suffix}: `, (answer) => {
49-
rl.close()
50-
resolve(answer.trim() || defaultValue || "")
51-
})
52-
})
53-
}
54-
5525
export async function submitResults(data: SubmitData): Promise<string | null> {
56-
// Confirm/fill terminal info
57-
console.log(`\n\x1b[1mConfirm submission details:\x1b[0m`)
58-
data.terminal = await prompt(" Terminal name", data.terminal)
59-
data.terminalVersion = await prompt(" Terminal version", data.terminalVersion || undefined)
60-
data.os = await prompt(" Operating system", data.os)
26+
const passed = Object.values(data.results).filter(Boolean).length
27+
const total = Object.keys(data.results).length
28+
const pct = Math.round((passed / total) * 100)
29+
const ver = data.terminalVersion ? ` ${data.terminalVersion}` : ""
30+
31+
console.log(`\n Submitting: \x1b[1m${data.terminal}${ver}\x1b[0m on ${data.os}${pct}% (${passed}/${total})`)
6132

6233
if (!data.terminalVersion) {
63-
console.log(`\x1b[33m ⚠ No version specifiedresults will be less useful\x1b[0m`)
34+
console.log(` \x1b[33m⚠ No version detecteduse --terminal-version to specify\x1b[0m`)
6435
}
6536

6637
// Check for duplicates
6738
if (hasGhCli()) {
6839
const existing = checkDuplicate(data.terminal, data.terminalVersion, data.os)
6940
if (existing) {
70-
console.log(`\n\x1b[33m⚠ A submission already exists for ${data.terminal}${data.terminalVersion ? ` ${data.terminalVersion}` : ""} on ${data.os}:\x1b[0m`)
71-
console.log(` ${existing}`)
72-
const proceed = await prompt(" Submit anyway? (y/N)", "N")
73-
if (proceed.toLowerCase() !== "y") {
74-
console.log(`Skipped.`)
75-
return null
76-
}
41+
console.log(` \x1b[33m⚠ Similar submission exists: ${existing}\x1b[0m`)
42+
console.log(` Submitting anyway (different probe version may have new results)`)
7743
}
7844
}
7945

80-
const passed = Object.values(data.results).filter(Boolean).length
81-
const total = Object.keys(data.results).length
82-
const pct = Math.round((passed / total) * 100)
83-
84-
const title = `[census] ${data.terminal}${data.terminalVersion ? ` ${data.terminalVersion}` : ""} on ${data.os}${pct}% (${passed}/${total})`
46+
const title = `[census] ${data.terminal}${ver} on ${data.os}${pct}% (${passed}/${total})`
8547

8648
const body = `## Community Census Result
8749
@@ -114,8 +76,8 @@ ${JSON.stringify(data, null, 2)}
11476
if (!hasGhCli()) {
11577
const filename = `terminfo-${data.terminal}-${data.os}-${Date.now()}.json`
11678
writeFileSync(filename, JSON.stringify(data, null, 2))
117-
console.log(`\n\x1b[33mgh CLI not found. Results saved to ${filename}\x1b[0m`)
118-
console.log(`To submit: https://github.com/${REPO}/issues/new`)
79+
console.log(`\n \x1b[33mgh CLI not found. Results saved to ${filename}\x1b[0m`)
80+
console.log(` To submit: https://github.com/${REPO}/issues/new`)
11981
return null
12082
}
12183

@@ -130,8 +92,8 @@ ${JSON.stringify(data, null, 2)}
13092
], { encoding: "utf-8", timeout: 30000 })
13193
return result.trim()
13294
} catch (err) {
133-
console.error(`\x1b[31mFailed to create issue\x1b[0m`)
134-
console.error(err instanceof Error ? err.message : String(err))
95+
console.error(` \x1b[31mFailed to create issue\x1b[0m`)
96+
console.error(` ${err instanceof Error ? err.message : String(err)}`)
13597
return null
13698
} finally {
13799
try { unlinkSync(bodyFile) } catch {}
@@ -159,8 +121,7 @@ function checkDuplicate(terminal: string, version: string, os: string): string |
159121
"--json", "url,title",
160122
"--jq", ".[0] | .title + \" \" + .url",
161123
], { encoding: "utf-8", timeout: 10000 })
162-
const trimmed = result.trim()
163-
return trimmed || null
124+
return result.trim() || null
164125
} catch {
165126
return null
166127
}

0 commit comments

Comments
 (0)