@@ -23,12 +23,18 @@ import select
2323import shutil
2424import subprocess
2525import sys
26- import termios
2726import time
28- import tty
2927import urllib .request
3028from urllib .parse import urlparse
3129
30+ if os .name == "nt" :
31+ import msvcrt
32+ termios = None
33+ tty = None
34+ else :
35+ import termios
36+ import tty
37+
3238try :
3339 import readline # noqa: F401 启用 input() 的行编辑 (退格/CJK 宽度/Ctrl-U/方向键)
3440except ImportError :
@@ -486,6 +492,8 @@ def read_input(prompt: str) -> str | None:
486492 first = input (prompt )
487493 except EOFError :
488494 return None
495+ if os .name == "nt" :
496+ return first
489497 chunks = [first ]
490498 deadline = time .time () + 0.2
491499 while True :
@@ -522,7 +530,7 @@ def ask_choice(prompt: str, n: int, default: int | None = None) -> int | None:
522530 print (color (f" 请输入 1..{ n } 之间的整数" , C_YELLOW ))
523531
524532
525- # ==================== TUI 菜单(基于 termios,参考 codex-provider-macos )====================
533+ # ==================== TUI 菜单(Unix termios / Windows msvcrt )====================
526534
527535def clear_screen () -> None :
528536 if not sys .stdout .isatty ():
@@ -535,6 +543,22 @@ def read_key() -> str:
535543 """阻塞读一个键。返回: up / down / enter / quit / back / 或单字符串。"""
536544 if not sys .stdin .isatty ():
537545 return "quit"
546+ if os .name == "nt" :
547+ ch = msvcrt .getwch ()
548+ if ch in ("\x00 " , "\xe0 " ):
549+ code = msvcrt .getwch ()
550+ if code in ("H" , "K" ):
551+ return "up"
552+ if code in ("P" , "M" ):
553+ return "down"
554+ return "back"
555+ if ch in ("\r " , "\n " ):
556+ return "enter"
557+ if ch in ("q" , "Q" , "\x03 " ):
558+ return "quit"
559+ if ch in ("b" , "B" , "\x08 " ):
560+ return "back"
561+ return ""
538562 fd = sys .stdin .fileno ()
539563 old = termios .tcgetattr (fd )
540564 try :
@@ -985,9 +1009,9 @@ def cmd_export(token: str, cfg: dict) -> None:
9851009 print (color ("shell 片段:" , C_BOLD ))
9861010 for ln in snippet .splitlines ():
9871011 print (f" { ln } " )
988- # pbcopy
1012+ clipboard_cmd = [ "clip.exe" ] if os . name == "nt" else [ " pbcopy" ]
9891013 try :
990- subprocess .run ([ "pbcopy" ] , input = snippet , text = True , check = True , timeout = 3 )
1014+ subprocess .run (clipboard_cmd , input = snippet , text = True , check = True , timeout = 3 )
9911015 print (color (" ✓ 已复制到剪贴板" , C_GREEN ))
9921016 except (subprocess .CalledProcessError , FileNotFoundError , subprocess .TimeoutExpired ):
9931017 print (color (" (剪贴板复制失败,自己粘上方文本)" , C_DIM ))
@@ -998,12 +1022,13 @@ def _resolve_inject_bin() -> str | None:
9981022 here = os .path .dirname (os .path .abspath (__file__ ))
9991023 candidates = [
10001024 os .path .join (here , "sub2cli-inject" ),
1025+ os .path .join (here , "sub2cli-inject.cmd" ),
10011026 os .path .expanduser ("~/.local/bin/sub2cli-inject" ),
10021027 os .path .expanduser ("~/.local/bin/codex-provider" ), # 兼容已装 codex-provider 的用户
10031028 shutil .which ("sub2cli-inject" ),
10041029 shutil .which ("codex-provider" ),
10051030 ]
1006- return next ((p for p in candidates if p and os .path .isfile (p ) and os .access (p , os .X_OK )), None )
1031+ return next ((p for p in candidates if p and os .path .isfile (p ) and ( os .name == "nt" or os . access (p , os .X_OK ) )), None )
10071032
10081033
10091034def _run_inject (inject_bin : str , url : str , api_key : str , source_label : str ) -> None :
0 commit comments