File size: 4,501 Bytes
00d812d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/usr/bin/env bun
/**
 * Terminal capability detection diagnostic test.
 *
 * Shows what ink-picture detects about the current terminal:
 * env vars, escape sequence query results, and the final protocol.
 *
 * Usage:
 *   bun run src/ink-picture/__tests__/TerminalDetection.test.tsx
 */

import React, { useEffect, useState } from 'react'
import { render, Box, Text, useApp } from 'ink'
import Image, {
  InkPictureProvider,
  type TerminalInfo,
  useTerminalInfo,
} from '../index.ts'

/**
 * Probe Kitty graphics protocol directly via raw escape sequences.
 * Uses fs.writeSync to stdout and intercepts stdin responses,
 * bypassing Ink's input pipeline.
 */
async function probeKittyDirect(): Promise<{ protocol: string }> {
  return new Promise(resolve => {
    const result = { protocol: 'no-response' }

    if (!process.stdout.isTTY || !process.stdin.isTTY) {
      resolve(result)
      return
    }

    const origPush = process.stdin.push.bind(process.stdin)
    let resolved = false

    const finish = (label: string) => {
      if (resolved) return
      resolved = true
      process.stdin.push = origPush as any
      result.protocol = label
      resolve(result)
    }

    process.stdin.push = ((chunk: any) => {
      const str = Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk)

      // Kitty OK response
      if (/\x1b_Gi=31;OK\x1b\\/.test(str)) {
        finish('kitty-ok')
        return origPush(chunk)
      }

      // DA1 sentinel — kitty response should arrive before or with DA1
      if (/\x1b\[\?(\d+(?:;\d+)*)c/.test(str)) {
        // Give kitty response a chance if it arrived in same chunk after DA
        setTimeout(() => finish('no-kitty'), 200)
      }

      return origPush(chunk)
    }) as any

    const query =
      '\x1b[8m' +
      '\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\' +
      '\x1b[c' +
      '\x1b[2K\r' +
      '\x1b[0m'

    try {
      process.stdin.setRawMode?.(true)
      require('fs').writeSync(process.stdout.fd, query)
    } catch {}

    setTimeout(() => finish('timeout'), 2000)
  })
}

function DetectionApp() {
  const { exit } = useApp()
  const terminalInfo = useTerminalInfo()
  const [probeResult, setProbeResult] = useState<string>('probing...')

  useEffect(() => {
    probeKittyDirect().then(r => {
      setProbeResult(JSON.stringify(r))
    })
    const timer = setTimeout(() => exit(), 5000)
    return () => clearTimeout(timer)
  }, [exit])

  const env: Record<string, string | undefined> = {
    TERM_PROGRAM: process.env.TERM_PROGRAM,
    TERM: process.env.TERM,
    KITTY_WINDOW_ID: process.env.KITTY_WINDOW_ID,
    GHOSTTY_RESOURCES_DIR: process.env.GHOSTTY_RESOURCES_DIR,
    TERM_PROGRAM_VERSION: process.env.TERM_PROGRAM_VERSION,
    COLORTERM: process.env.COLORTERM,
    WEZTERM_PANE: process.env.WEZTERM_PANE,
    KONSOLE_VERSION: process.env.KONSOLE_VERSION,
    XTERM_VERSION: process.env.XTERM_VERSION,
    VTE_VERSION: process.env.VTE_VERSION,
  }

  return (
    <Box flexDirection="column" paddingX={1}>
      <Text bold underline color="cyan">
        Terminal Detection Diagnostic
      </Text>
      <Text>{' '}</Text>

      <Text bold color="yellow">
        Environment Variables:
      </Text>
      {Object.entries(env).map(([k, v]) => (
        <Text key={k}>
          {'  '}
          {k}: {v ?? '(not set)'}
        </Text>
      ))}
      <Text>{' '}</Text>

      <Text bold color="yellow">
        Direct Kitty Probe (raw escape sequence):
      </Text>
      <Text>  {probeResult}</Text>
      <Text>{' '}</Text>

      <Text bold color="yellow">
        InkPictureProvider TerminalInfo:
      </Text>
      <Text>
        {'  '}supportsKittyGraphics: {String(terminalInfo.supportsKittyGraphics)}
      </Text>
      <Text>
        {'  '}supportsSixelGraphics: {String(terminalInfo.supportsSixelGraphics)}
      </Text>
      <Text>
        {'  '}supportsITerm2Graphics: {String(terminalInfo.supportsITerm2Graphics)}
      </Text>
      <Text>
        {'  '}supportsUnicode: {String(terminalInfo.supportsUnicode)}
      </Text>
      <Text>
        {'  '}supportsColor: {String(terminalInfo.supportsColor)}
      </Text>
      <Text>
        {'  '}cell: {terminalInfo.cellWidth}x{terminalInfo.cellHeight}
      </Text>
      <Text>
        {'  '}terminal: {terminalInfo.terminalWidth}x{terminalInfo.terminalHeight}
      </Text>
    </Box>
  )
}

const { waitUntilExit } = render(
  <InkPictureProvider>
    <DetectionApp />
  </InkPictureProvider>,
)
await waitUntilExit()