Skip to content

Commit 6a792c8

Browse files
committed
fix: cross-platform onboarding wizard and LF line endings
onboard_ui.py imported tty and termios unconditionally. Both are Unix-only, so the wizard crashed on import under Windows Python before any logic ran. Branch the raw key reader on sys.platform: use msvcrt.getwch on Windows (handling the 0x00 / 0xe0 arrow-key prefix), keep the existing tty/termios path on POSIX. Add .gitattributes forcing LF on .sh and .py so Git core.autocrlf=true on Windows clones does not rewrite shell script shebangs to CRLF (previously caused /usr/bin/env: 'bash\r': No such file or directory when invoking install.sh from Git Bash or WSL).
1 parent 6f8cfbd commit 6a792c8

2 files changed

Lines changed: 41 additions & 19 deletions

File tree

.gitattributes

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Force LF line endings for shell scripts and Python so Windows clones
2+
# (with core.autocrlf=true) don't break shebang parsing on bash/WSL.
3+
*.sh text eol=lf
4+
*.py text eol=lf

onboard_ui.py

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
"""ANSI palette, block-char banner, and clack-style layout atoms (stdlib only)."""
2-
import sys, os, shutil, tty, termios
2+
import sys, os, shutil
3+
4+
_WIN = sys.platform == "win32"
5+
if _WIN:
6+
import msvcrt
7+
else:
8+
import tty, termios
39

410
# ── Palette ───────────────────────────────────────────────────────────────
511
def _e(*c): return f"\x1b[{';'.join(map(str,c))}m"
@@ -58,22 +64,34 @@ def outro(lines):
5864
print(f"{MUTED}{R}\n")
5965

6066
# ── Raw key reader ────────────────────────────────────────────────────────
61-
def _getch():
62-
fd = sys.stdin.fileno()
63-
old = termios.tcgetattr(fd)
64-
try:
65-
tty.setraw(fd)
66-
return sys.stdin.buffer.read(1).decode("utf-8", errors="replace")
67-
finally:
68-
termios.tcsetattr(fd, termios.TCSADRAIN, old)
67+
if _WIN:
68+
def get_key():
69+
ch = msvcrt.getwch()
70+
if ch in ("\x00", "\xe0"): # arrow-key / function-key prefix
71+
c2 = msvcrt.getwch()
72+
return {"H": "UP", "P": "DOWN", "K": "LEFT", "M": "RIGHT"}.get(c2, "ESC")
73+
if ch in "\r\n": return "ENTER"
74+
if ch == "\x03": raise KeyboardInterrupt
75+
if ch == "\x08": return "BACKSPACE"
76+
if ch == "\x1b": return "ESC"
77+
return ch
78+
else:
79+
def _getch():
80+
fd = sys.stdin.fileno()
81+
old = termios.tcgetattr(fd)
82+
try:
83+
tty.setraw(fd)
84+
return sys.stdin.buffer.read(1).decode("utf-8", errors="replace")
85+
finally:
86+
termios.tcsetattr(fd, termios.TCSADRAIN, old)
6987

70-
def get_key():
71-
ch = _getch()
72-
if ch == "\x1b":
73-
_getch() # consume '['
74-
c2 = _getch()
75-
return {"A": "UP", "B": "DOWN", "C": "RIGHT", "D": "LEFT"}.get(c2, "ESC")
76-
if ch in "\r\n": return "ENTER"
77-
if ch == "\x03": raise KeyboardInterrupt
78-
if ch == "\x7f": return "BACKSPACE"
79-
return ch
88+
def get_key():
89+
ch = _getch()
90+
if ch == "\x1b":
91+
_getch() # consume '['
92+
c2 = _getch()
93+
return {"A": "UP", "B": "DOWN", "C": "RIGHT", "D": "LEFT"}.get(c2, "ESC")
94+
if ch in "\r\n": return "ENTER"
95+
if ch == "\x03": raise KeyboardInterrupt
96+
if ch == "\x7f": return "BACKSPACE"
97+
return ch

0 commit comments

Comments
 (0)