feat: Add MultiSelect widget to chatmark library

- Implement MultiSelect class in widgets.py
- Update __init__.py to export MultiSelect
- Add support for multiple option selection with checkboxes
This commit is contained in:
bobo.yang 2024-11-14 18:45:59 +08:00
parent 6d34ceb8e2
commit 014579d557
2 changed files with 90 additions and 1 deletions

View File

@ -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 @@ __all__ = [
"Button",
"Form",
"Step",
"MultiSelect",
]

View File

@ -162,6 +162,94 @@ class Checkbox(Widget):
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):
"""