(opts: { timeoutMs: number })
| 57 | } |
| 58 | |
| 59 | async function queryOsc11(opts: { timeoutMs: number }): Promise<ResolvedTheme | null> { |
| 60 | const stdin = process.stdin as unknown as RawModeStdin; |
| 61 | if (typeof stdin.setRawMode !== "function") return null; |
| 62 | // If something else is already listening on stdin (e.g. another raw-mode |
| 63 | // consumer), don't fight for it — punt to COLORFGBG instead. |
| 64 | if (process.stdin.listenerCount("data") > 0) return null; |
| 65 | |
| 66 | const wasRaw = stdin.isRaw === true; |
| 67 | let buffer = ""; |
| 68 | let listener: ((data: Buffer) => void) | null = null; |
| 69 | let timer: NodeJS.Timeout | null = null; |
| 70 | |
| 71 | try { |
| 72 | if (!wasRaw) stdin.setRawMode(true); |
| 73 | |
| 74 | const result = await new Promise<ResolvedTheme | null>((resolve) => { |
| 75 | listener = (chunk: Buffer): void => { |
| 76 | buffer += chunk.toString("utf8"); |
| 77 | const theme = parseOsc11BackgroundTheme(buffer); |
| 78 | if (theme !== null) resolve(theme); |
| 79 | }; |
| 80 | stdin.on("data", listener); |
| 81 | timer = setTimeout(() => { |
| 82 | resolve(null); |
| 83 | }, opts.timeoutMs); |
| 84 | try { |
| 85 | process.stdout.write(OSC11_QUERY); |
| 86 | } catch { |
| 87 | resolve(null); |
| 88 | } |
| 89 | }); |
| 90 | |
| 91 | return result; |
| 92 | } catch { |
| 93 | return null; |
| 94 | } finally { |
| 95 | if (timer !== null) clearTimeout(timer); |
| 96 | if (listener !== null) stdin.off("data", listener); |
| 97 | if (!wasRaw) { |
| 98 | try { |
| 99 | stdin.setRawMode(false); |
| 100 | } catch { |
| 101 | /* ignore — raw mode restoration best-effort */ |
| 102 | } |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | /** |
| 108 | * COLORFGBG is `"fg;bg"` (sometimes `"fg;default;bg"`). The last token is |
no test coverage detected