terminfo.dev 0.7.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminfo.dev",
3
- "version": "0.7.0",
3
+ "version": "1.0.0",
4
4
  "description": "Test your terminal's feature support and contribute to terminfo.dev",
5
5
  "keywords": [
6
6
  "ansi",
@@ -32,6 +32,9 @@
32
32
  "publishConfig": {
33
33
  "access": "public"
34
34
  },
35
+ "dependencies": {
36
+ "silvery": "^0.4.3"
37
+ },
35
38
  "engines": {
36
39
  "node": ">=23.6.0"
37
40
  }
package/src/index.ts CHANGED
@@ -44,11 +44,6 @@ function loadFeatureSlugs(): Record<string, string> {
44
44
  return {} // fallback: featureSlug() will use id.replaceAll(".", "-")
45
45
  }
46
46
 
47
- /** OSC 8 hyperlink — clickable link in supporting terminals */
48
- function link(url: string, text: string): string {
49
- return `\x1b]8;;${url}\x07${text}\x1b]8;;\x07`
50
- }
51
-
52
47
  interface ResultEntry {
53
48
  terminal: string
54
49
  terminalVersion: string
@@ -69,19 +64,6 @@ async function main() {
69
64
  // Detect terminal
70
65
  const terminal = detectTerminal()
71
66
 
72
- if (!jsonMode) {
73
- console.log(`\x1b[1mterminfo.dev\x1b[0m — can your terminal do that?\n`)
74
- console.log(` Terminal: \x1b[1m${terminal.name}\x1b[0m${terminal.version ? ` ${terminal.version}` : ""}`)
75
- console.log(` Platform: ${terminal.os}${terminal.osVersion ? ` ${terminal.osVersion}` : ""}`)
76
- console.log(` Probes: ${ALL_PROBES.length} features across ${new Set(ALL_PROBES.map(p => p.id.split(".")[0])).size} categories`)
77
- console.log(` Website: https://terminfo.dev`)
78
- console.log(``)
79
- console.log(`\x1b[2mResults are compared against ${ALL_PROBES.length} terminal features from the`)
80
- console.log(`ECMA-48, VT100/VT510, xterm, and Kitty specifications.`)
81
- console.log(`Run with --submit to contribute your results to the database.\x1b[0m\n`)
82
- console.log(`Running probes...`)
83
- }
84
-
85
67
  const results: Record<string, boolean> = {}
86
68
  const notes: Record<string, string> = {}
87
69
  const responses: Record<string, string> = {}
@@ -134,10 +116,7 @@ async function main() {
134
116
  return
135
117
  }
136
118
 
137
- // Display results
138
- console.log(`\n\x1b[1mResults: ${passed}/${total} (${pct}%)\x1b[0m\n`)
139
-
140
- // Show categories with OSC 8 hyperlinks
119
+ // Build category data for report
141
120
  const slugs = loadFeatureSlugs()
142
121
  const categories = new Map<string, Array<{ id: string; name: string; pass: boolean; note?: string }>>()
143
122
  for (const probe of ALL_PROBES) {
@@ -151,20 +130,22 @@ async function main() {
151
130
  })
152
131
  }
153
132
 
154
- for (const [cat, probes] of categories) {
155
- const catPassed = probes.filter((p) => p.pass).length
156
- const color = catPassed === probes.length ? "\x1b[32m" : catPassed > 0 ? "\x1b[33m" : "\x1b[31m"
157
- const catLink = link(`https://terminfo.dev/${cat}`, cat)
158
- console.log(`${color}${catLink}\x1b[0m (${catPassed}/${probes.length})`)
159
- for (const p of probes) {
160
- const icon = p.pass ? "\x1b[32m✓\x1b[0m" : "\x1b[31m✗\x1b[0m"
161
- const note = p.note ? ` \x1b[2m— ${p.note}\x1b[0m` : ""
162
- const slug = slugs[p.id] ?? p.id.replaceAll(".", "-")
163
- const cat = p.id.split(".")[0]!
164
- const featureLink = link(`https://terminfo.dev/${cat}/${slug}`, p.name)
165
- console.log(` ${icon} ${featureLink}${note}`)
166
- }
167
- }
133
+ // Render with silvery
134
+ const { renderReport } = await import("./report.tsx")
135
+ const output = await renderReport({
136
+ terminal: terminal.name,
137
+ terminalVersion: terminal.version,
138
+ os: terminal.os,
139
+ osVersion: terminal.osVersion,
140
+ probeCount: total,
141
+ categoryCount: new Set(ALL_PROBES.map(p => p.id.split(".")[0])).size,
142
+ passed,
143
+ total,
144
+ categories,
145
+ slugs,
146
+ submitMode,
147
+ })
148
+ console.log(output)
168
149
 
169
150
  if (submitMode) {
170
151
  console.log(`\nSubmitting results to terminfo.dev...`)
@@ -172,9 +153,6 @@ async function main() {
172
153
  if (url) {
173
154
  console.log(`\x1b[32m✓ Issue created:\x1b[0m ${url}`)
174
155
  }
175
- } else {
176
- console.log(`\n\x1b[2mSubmit results to terminfo.dev: npx terminfo --submit\x1b[0m`)
177
- console.log(`\x1b[2mJSON output: npx terminfo --json\x1b[0m`)
178
156
  }
179
157
  }
180
158
 
package/src/report.tsx ADDED
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Silvery-rendered CLI report for terminfo probe results.
3
+ */
4
+ import React from "react"
5
+ import { Box, Text } from "silvery"
6
+ import { renderString } from "silvery"
7
+
8
+ interface ProbeResult {
9
+ id: string
10
+ name: string
11
+ pass: boolean
12
+ note?: string
13
+ }
14
+
15
+ interface ReportProps {
16
+ terminal: string
17
+ terminalVersion: string
18
+ os: string
19
+ osVersion: string
20
+ probeCount: number
21
+ categoryCount: number
22
+ passed: number
23
+ total: number
24
+ categories: Map<string, ProbeResult[]>
25
+ }
26
+
27
+ /** OSC 8 hyperlink wrapper */
28
+ function osc8(url: string, text: string): string {
29
+ return `\x1b]8;;${url}\x07${text}\x1b]8;;\x07`
30
+ }
31
+
32
+ function featureUrl(id: string, slug: string): string {
33
+ const cat = id.split(".")[0]!
34
+ return `https://terminfo.dev/${cat}/${slug}`
35
+ }
36
+
37
+ function Header({ terminal, terminalVersion, os, osVersion, probeCount, categoryCount, passed, total }: ReportProps) {
38
+ const pct = Math.round((passed / total) * 100)
39
+ return (
40
+ <Box flexDirection="column" marginBottom={1}>
41
+ <Text bold>{osc8("https://terminfo.dev", "terminfo.dev")}</Text>
42
+ <Text dimColor>Can your terminal do that?</Text>
43
+ <Text> </Text>
44
+ <Box flexDirection="row" gap={2}>
45
+ <Box flexDirection="column" width={12}>
46
+ <Text dimColor>Terminal</Text>
47
+ <Text dimColor>Platform</Text>
48
+ <Text dimColor>Probes</Text>
49
+ <Text dimColor>Score</Text>
50
+ </Box>
51
+ <Box flexDirection="column">
52
+ <Text bold>{terminal}{terminalVersion ? ` ${terminalVersion}` : ""}</Text>
53
+ <Text>{os} {osVersion}</Text>
54
+ <Text>{probeCount} features, {categoryCount} categories</Text>
55
+ <Text bold color={pct === 100 ? "green" : pct >= 90 ? "yellow" : "red"}>{passed}/{total} ({pct}%)</Text>
56
+ </Box>
57
+ </Box>
58
+ </Box>
59
+ )
60
+ }
61
+
62
+ function CategorySection({ name, probes, slugs }: { name: string; probes: ProbeResult[]; slugs: Record<string, string> }) {
63
+ const catPassed = probes.filter(p => p.pass).length
64
+ const allPassed = catPassed === probes.length
65
+ const catUrl = `https://terminfo.dev/${name}`
66
+
67
+ return (
68
+ <Box flexDirection="column">
69
+ <Text color={allPassed ? "green" : catPassed > 0 ? "yellow" : "red"}>
70
+ {osc8(catUrl, name)} ({catPassed}/{probes.length})
71
+ </Text>
72
+ {probes.map(p => {
73
+ const slug = slugs[p.id] ?? p.id.replaceAll(".", "-")
74
+ const url = featureUrl(p.id, slug)
75
+ const icon = p.pass ? "✓" : "✗"
76
+ return (
77
+ <Box key={p.id} flexDirection="row" paddingLeft={2}>
78
+ <Text color={p.pass ? "green" : "red"}>{icon} </Text>
79
+ <Text>{osc8(url, p.name)}</Text>
80
+ {p.note && <Text dimColor> — {p.note}</Text>}
81
+ </Box>
82
+ )
83
+ })}
84
+ </Box>
85
+ )
86
+ }
87
+
88
+ function Footer({ submitMode }: { submitMode: boolean }) {
89
+ if (submitMode) return null
90
+ return (
91
+ <Box flexDirection="column" marginTop={1}>
92
+ <Text dimColor>Submit: npx terminfo.dev --submit</Text>
93
+ <Text dimColor>JSON: npx terminfo.dev --json</Text>
94
+ </Box>
95
+ )
96
+ }
97
+
98
+ function Report(props: ReportProps & { slugs: Record<string, string>; submitMode: boolean }) {
99
+ return (
100
+ <Box flexDirection="column">
101
+ <Header {...props} />
102
+ {[...props.categories.entries()].map(([name, probes]) => (
103
+ <CategorySection key={name} name={name} probes={probes} slugs={props.slugs} />
104
+ ))}
105
+ <Footer submitMode={props.submitMode} />
106
+ </Box>
107
+ )
108
+ }
109
+
110
+ export async function renderReport(props: ReportProps & { slugs: Record<string, string>; submitMode: boolean }): Promise<string> {
111
+ const width = process.stdout.columns ?? 80
112
+ return renderString(<Report {...props} />, { width })
113
+ }