diff --git a/app.py b/app.py index f04605f..3fec2fd 100644 --- a/app.py +++ b/app.py @@ -7992,6 +7992,22 @@ def build_access_window_handoff(access_url, access_token): 'title': f'{APP_NAME} Access ({get_runtime_name()})', } +def access_window_python_supports_tk(command): + # Apple's bundled Tk 8.5 opens a window but renders no widgets, so a + # too-old Tk is worse than no window at all. The non-WSL launch command + # reuses this interpreter, so check tkinter in-process; helper Pythons + # (WSL python.exe) ship a modern Tk and are not probed. + if not command or command[0] != sys.executable: + return True + try: + import tkinter + except Exception: + return False + try: + return float(tkinter.TkVersion) >= 8.6 + except (TypeError, ValueError): + return False + def start_access_window(access_url, access_token): if not access_window_enabled(): return False @@ -7999,6 +8015,11 @@ def start_access_window(access_url, access_token): if not command: print('[!] Access window unavailable: no suitable Python/Tk runtime found.', flush=True) return False + if not access_window_python_supports_tk(command): + print('[!] Access window skipped: tkinter with Tk 8.6+ is required.', flush=True) + print(' macOS: brew install python3 python-tk, or sudo port install python313 py313-tkinter tk +quartz,', flush=True) + print(' then delete tools/.venv_* and rerun the launcher. Use the Access URL/Token above meanwhile.', flush=True) + return False handoff = build_access_window_handoff(access_url, access_token) diff --git a/install.command b/install.command new file mode 100755 index 0000000..a63a875 --- /dev/null +++ b/install.command @@ -0,0 +1,2 @@ +#!/bin/bash +exec "$(dirname "$0")/install.sh" "$@" diff --git a/run.command b/run.command new file mode 100755 index 0000000..e469628 --- /dev/null +++ b/run.command @@ -0,0 +1,2 @@ +#!/bin/bash +exec "$(dirname "$0")/run.sh" "$@" diff --git a/run.sh b/run.sh index 1aba54e..f5b6936 100755 --- a/run.sh +++ b/run.sh @@ -58,7 +58,9 @@ echo "========================================" print_python_install_help() { echo "[!] ERROR: python3 is required but not found." echo " Install with: sudo apt install python3 python3-venv python3-pip (Debian/Ubuntu/WSL)" - echo " brew install python3 (macOS)" + echo " brew install python3 python-tk (macOS Homebrew)" + echo " sudo port install python313 py313-tkinter tk +quartz (macOS MacPorts)" + echo " macOS note: the access window needs Tk 8.6+; Apple's bundled Python 3.9/Tk 8.5 is too old." echo " Or point the launcher at a manually prepared Python 3.10+ runtime:" echo " STANDTERM_PYTHON=/path/to/python3 ./run.sh --force" echo " Or manually create the launcher venv, then rerun ./run.sh:" @@ -303,6 +305,16 @@ if ! venv_activation_is_current; then source "$VENV_DIR/bin/activate" || exit 1 venv_activation_is_current || exit 1 fi +if ! python_is_usable python; then + echo "[!] Virtual environment Python is older than 3.10 (likely created before the version requirement)." + ensure_bootstrap_python || exit 1 + echo "[*] Recreating virtual environment automatically..." + remove_venv_dir + create_venv + FORCE_RECHECK=true + source "$VENV_DIR/bin/activate" || exit 1 + venv_activation_is_current || exit 1 +fi echo "[*] Checking Python dependencies..." if ! verify_dependencies; then diff --git a/scripts/access_window.py b/scripts/access_window.py index 2205ad3..b4a0f16 100644 --- a/scripts/access_window.py +++ b/scripts/access_window.py @@ -452,11 +452,28 @@ def parse_args(argv): return args +def tk_runtime_supported(): + # Apple's bundled Tk 8.5 opens a window but renders no widgets on modern + # macOS, so refuse to show a broken blank window on anything below 8.6. + try: + return float(tk.TkVersion) >= 8.6 + except (TypeError, ValueError): + return False + + def main(argv=None): args = parse_args(sys.argv[1:] if argv is None else argv) + if not tk_runtime_supported(): + print( + f'StandTerm access window requires Tk 8.6+; found Tk {tk.TkVersion}. ' + 'Use the Access URL/Token from the launcher console instead.', + file=sys.stderr, + ) + return 3 root = build_window(args) root.mainloop() + return 0 if __name__ == '__main__': - main() + sys.exit(main())