diff --git a/README.md b/README.md index 79acff0..e802c5c 100644 --- a/README.md +++ b/README.md @@ -121,13 +121,13 @@ cd ~/Documents zmk init ``` -Follow the instructions it gives you. If you already have a ZMK config repo, you can enter its URL when prompted, for example: +Follow the instructions it gives you. If you already have a ZMK config repo, select "Clone an existing repo" and enter the repo's URL, for example: ``` Repository URL: https://github.com/myusername/zmk-config ``` -Otherwise, leave this first prompt blank and press Enter, and it will walk you through creating a new repo. +Otherwise, select "Create a new ZMK config repo" and it will walk you through creating a new repo. Once you finish following all the instructions, you will have a copy of the repo stored on your computer. All `zmk` commands will run on this repo (unless the working directory is inside a different repo). If you ever forget where the repo is located, you can run `zmk cd` to find it. diff --git a/zmk/commands/init.py b/zmk/commands/init.py index c56d77c..cbee942 100644 --- a/zmk/commands/init.py +++ b/zmk/commands/init.py @@ -19,6 +19,7 @@ from ..build import BuildMatrix from ..config import Config, get_config, set_context_repo from ..exceptions import FatalError +from ..menu import show_menu from ..prompt import UrlPrompt from ..repo import Repo, find_containing_repo, is_repo from .keyboard.add import keyboard_add @@ -139,26 +140,29 @@ def _check_for_existing_repo(cfg: Config): if cfg.home_path and is_repo(cfg.home_path): rich.print(f'You already have a ZMK config repo at "{cfg.home_path}".') - if not Confirm.ask("Create a new repo?", default=False): + if not Confirm.ask("Initialize a new repo?", default=False): raise typer.Exit() + rich.print() -def _get_repo_url(): - rich.print( - "If you already have a ZMK config repo, enter its URL here. " - "Otherwise, leave this blank to create a new repo." - ) - url = Prompt.ask("Repository URL") - if url: - return url +_CREATE_NEW_REPO = "Create a new ZMK config repo" +_CLONE_REPO = "Clone an existing repo" + +def _get_repo_url(): console = rich.get_console() + + response = show_menu(title=None, items=[_CREATE_NEW_REPO, _CLONE_REPO]) + + if response == _CLONE_REPO: + console.print("Enter the URL of your ZMK config repo.") + return UrlPrompt.ask("Repository URL") + console.print( - "\n" - "To create your ZMK config repo, we will open a GitHub page in your " + "To create your ZMK config repo, we will open a GitHub page in your web " "browser to create a repo from a template. Log in to GitHub if necessary, " - "then click the green [green]Create repository[/green] button. " + "then click the green [green]Create repository[/green] button." "\n\n" "Once it finishes creating the repo, Click the green [green]<> Code[/green] " "button, then copy the HTTPS URL and paste it here. " @@ -166,14 +170,21 @@ def _get_repo_url(): width=TEXT_WIDTH, ) input() - webbrowser.open(TEMPLATE_URL) - url = UrlPrompt.ask("Repository URL") - return url + if not webbrowser.open(TEMPLATE_URL): + console.print( + "[yellow]Failed to open a browser. Please open the following link in a web browser:" + ) + console.print("") + console.print(TEMPLATE_URL) + console.print("") + + return UrlPrompt.ask("Repository URL") def _get_directory_name(url: str): - default = urlparse(url).path.split("/")[-1] + # Default name is the last path component of the URL without any ".git" suffix + default = urlparse(url).path.split("/")[-1].removesuffix(".git") return Prompt.ask("Enter a directory name", default=default) diff --git a/zmk/menu.py b/zmk/menu.py index 9a2ccd7..23ab1a9 100644 --- a/zmk/menu.py +++ b/zmk/menu.py @@ -4,10 +4,10 @@ from collections.abc import Callable, Iterable from contextlib import contextmanager -from typing import Any, Generic, TypeVar +from typing import Generic, TypeVar import rich -from rich.console import Console +from rich.console import Console, RenderableType from rich.highlighter import Highlighter from rich.style import Style from rich.text import Text @@ -50,7 +50,7 @@ class TerminalMenu(Generic[T], Highlighter): } ) - title: Any + title: RenderableType | None items: list[T] default_index: int @@ -67,7 +67,7 @@ class TerminalMenu(Generic[T], Highlighter): def __init__( self, - title: Any, + title: RenderableType | None, items: Iterable[T], *, default_index=0, @@ -101,9 +101,13 @@ def __init__( self._focus_index = 0 self._scroll_index = 0 - title_lines = self.console.render_lines(self.title, pad=False) - self._num_title_lines = len(title_lines) - self._last_title_line_len = 1 + sum(s.cell_length for s in title_lines[-1]) + if self.title: + title_lines = self.console.render_lines(self.title, pad=False) + self._num_title_lines = len(title_lines) + self._last_title_line_len = 1 + sum(s.cell_length for s in title_lines[-1]) + else: + self._num_title_lines = 0 + self._last_title_line_len = 0 if self._get_display_count() == self._max_items_per_page: self._top_row = 1 @@ -202,11 +206,12 @@ def _apply_filter(self): self._clamp_focus_index() def _print_menu(self): - self.console.print( - f"[title]{self.title}[/title] [filter]{self._filter_text}[/filter]", - justify="left", - highlight=False, - ) + if self.title: + self.console.print( + f"[title]{self.title}[/title] [filter]{self._filter_text}[/filter]", + justify="left", + highlight=False, + ) display_count = self._get_display_count() @@ -404,7 +409,7 @@ def _erase_controls(self): def show_menu( - title: str, + title: RenderableType | None, items: Iterable[T], *, default_index=0,