diff --git a/lib/chatmark/__init__.py b/lib/chatmark/__init__.py index d78551f..07e0b93 100644 --- a/lib/chatmark/__init__.py +++ b/lib/chatmark/__init__.py @@ -1,6 +1,6 @@ from .form import Form from .step import Step -from .widgets import Button, Checkbox, Radio, TextEditor +from .widgets import Button, Checkbox, Radio, TextEditor, MultiSelect __all__ = [ "Checkbox", @@ -9,4 +9,5 @@ "Button", "Form", "Step", + "MultiSelect", ] diff --git a/lib/chatmark/widgets.py b/lib/chatmark/widgets.py index a8c1d90..120d421 100644 --- a/lib/chatmark/widgets.py +++ b/lib/chatmark/widgets.py @@ -162,6 +162,94 @@ def _parse_response(self, response: Dict): self._selections = selections +class multiSelect(Widget): + """ + ChatMark syntax: + ```chatmark + Which files would you like to commit? I've suggested a few. + > {x}(file1) devchat/engine/prompter.py + > {x}(file2) devchat/prompt.py + > {}(file3) tests/test_cli_prompt.py + ``` + + Response: + ```yaml + file1: checked + file3: checked + ``` + """ + + def __init__( + self, + options: List[str], + check_states: Optional[List[bool]] = None, + title: Optional[str] = None, + submit_button_name: str = "Submit", + cancel_button_name: str = "Cancel", + ): + """ + options: options to be selected + check_states: initial check states of options, default to all False + title: title of the widget + """ + super().__init__(submit_button_name, cancel_button_name) + + if check_states is not None: + assert len(options) == len(check_states) + else: + check_states = [False for _ in options] + + self._options = options + self._states = check_states + self._title = title + + self._selections: Optional[List[int]] = None + + @property + def selections(self) -> Optional[List[int]]: + """ + Get the indices of selected options + """ + return self._selections + + @property + def options(self) -> List[str]: + """ + Get the options + """ + return self._options + + def _in_chatmark(self) -> str: + """ + Generate ChatMark syntax for checkbox options + Use the index of option to generate id/key + """ + lines = [] + + if self._title: + lines.append(self._title) + + for idx, (option, state) in enumerate(zip(self._options, self._states)): + mark = "{x}" if state else "{}" + key = self.gen_id(self._id_prefix, idx) + lines.append(f"> {mark}({key}) {option}") + + text = "\n".join(lines) + return text + + def _parse_response(self, response: Dict): + selections = [] + for key, value in response.items(): + prefix, index = self.parse_id(key) + # check if the prefix is the same as the widget's + if prefix != self._id_prefix: + continue + + if value == "checked": + selections.append(index) + + self._selections = selections + class TextEditor(Widget): """