inquirer-textual 0.2.0__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,14 +10,15 @@ from textual.binding import Binding, BindingsMap
10
10
  from textual.widgets import Footer
11
11
 
12
12
  from inquirer_textual.common.InquirerHeader import InquirerHeader
13
- from inquirer_textual.common.Result import Result
13
+ from inquirer_textual.common.InquirerResult import InquirerResult
14
14
  from inquirer_textual.common.Shortcut import Shortcut
15
+ from inquirer_textual.common.StandardTheme import StandardTheme
15
16
  from inquirer_textual.widgets.InquirerWidget import InquirerWidget
16
17
 
17
18
  T = TypeVar('T')
18
19
 
19
20
 
20
- class InquirerApp(App[Result[T]], inherit_bindings=False): # type: ignore[call-arg]
21
+ class InquirerApp(App[InquirerResult[T]], inherit_bindings=False): # type: ignore[call-arg]
21
22
  CSS = """
22
23
  App {
23
24
  background: black;
@@ -40,7 +41,7 @@ class InquirerApp(App[Result[T]], inherit_bindings=False): # type: ignore[call-
40
41
  self.shortcuts: list[Shortcut] | None = None
41
42
  self.header: str | list[str] | None = None
42
43
  self.show_footer: bool = False
43
- self.result: Result[T] | None = None
44
+ self.result: InquirerResult[T] | None = None
44
45
  self.result_ready: Event | None = None
45
46
  self.inquiry_func: Callable[[InquirerApp[T]], None] | None = None
46
47
  self.inquiry_func_stop: bool = False
@@ -63,21 +64,24 @@ class InquirerApp(App[Result[T]], inherit_bindings=False): # type: ignore[call-
63
64
  if self.inquiry_func:
64
65
  self.inquiry_func(self)
65
66
 
66
- def action_shortcut(self, command: str):
67
+ async def action_shortcut(self, command: str):
67
68
  value = self.widget.current_value() if self.widget else None
68
- self._handle_result(command, value)
69
+ await self._handle_result(command, value)
69
70
 
70
71
  async def action_quit(self):
71
- self._handle_result('quit', None)
72
+ await self._handle_result('quit', None)
72
73
 
73
- def on_inquirer_widget_submit(self, event: InquirerWidget.Submit) -> None:
74
- self._handle_result(event.command, event.value)
74
+ async def on_inquirer_widget_submit(self, event: InquirerWidget.Submit) -> None:
75
+ await self._handle_result(event.command, event.value)
75
76
 
76
- def _handle_result(self, command: str | None, value: Any | None):
77
+ async def _handle_result(self, command: str | None, value: Any | None):
77
78
  if self.result_ready is not None:
78
- self.result = Result(command, value) # type: ignore[arg-type]
79
+ self.result = InquirerResult(self.widget.name if self.widget else None, value, # type: ignore[arg-type]
80
+ command)
79
81
  self.result_ready.set()
80
82
  else:
83
+ if self.widget:
84
+ await self.widget.set_selected_value(value)
81
85
  self.call_after_refresh(lambda: self._terminate(command, value))
82
86
 
83
87
  def _terminate(self, command: str | None = None, value: Any | None = None):
@@ -85,9 +89,9 @@ class InquirerApp(App[Result[T]], inherit_bindings=False): # type: ignore[call-
85
89
  if self.result_ready:
86
90
  self.result_ready.set()
87
91
  if command is not None:
88
- self.app.exit(Result(command, value))
92
+ self.app.exit(InquirerResult(self.widget.name if self.widget else None, value, command))
89
93
  else:
90
- self.exit(value)
94
+ self.exit(InquirerResult(None, value, None))
91
95
 
92
96
  def compose(self) -> ComposeResult:
93
97
  if self.header is not None:
@@ -102,7 +106,7 @@ class InquirerApp(App[Result[T]], inherit_bindings=False): # type: ignore[call-
102
106
  if self.widget:
103
107
  self.call_after_refresh(self.widget.focus)
104
108
 
105
- def prompt(self, widget: InquirerWidget, shortcuts: list[Shortcut] | None = None) -> Result[T]:
109
+ def prompt(self, widget: InquirerWidget, shortcuts: list[Shortcut] | None = None) -> InquirerResult[T]:
106
110
  if shortcuts:
107
111
  self.shortcuts = shortcuts
108
112
  self.show_footer = True
@@ -135,7 +139,7 @@ class InquirerApp(App[Result[T]], inherit_bindings=False): # type: ignore[call-
135
139
  auto_pilot: AutopilotCallbackType | None = None,
136
140
  loop: AbstractEventLoop | None = None,
137
141
  inquiry_func: Callable[[InquirerApp[T]], None] | None = None,
138
- ) -> Result[T]:
142
+ ) -> InquirerResult[T]:
139
143
  if not self.inquiry_func:
140
144
  self.inquiry_func = inquiry_func
141
145
  return super().run(
@@ -150,7 +154,9 @@ class InquirerApp(App[Result[T]], inherit_bindings=False): # type: ignore[call-
150
154
 
151
155
  def get_theme_variable_defaults(self) -> dict[str, str]:
152
156
  return {
153
- 'select-question-mark': '#e5c07b',
154
- 'select-list-item-highlight-foreground': '#61afef',
155
- 'input-color': '#98c379'
157
+ 'input-color': StandardTheme.input_color,
158
+ 'prompt-color': StandardTheme.prompt_color,
159
+ 'error-color': StandardTheme.error_color,
160
+ 'select-list-item-highlight-foreground': StandardTheme.select_list_item_highlight_foreground,
161
+ 'select-question-mark': StandardTheme.select_question_mark,
156
162
  }
@@ -0,0 +1,9 @@
1
+ from dataclasses import dataclass
2
+
3
+ from inquirer_textual.common.Shortcut import Shortcut
4
+
5
+
6
+ @dataclass
7
+ class AppConfig:
8
+ inline: bool = True
9
+ shortcuts: list[Shortcut] | None = None
@@ -7,3 +7,6 @@ class Choice:
7
7
  name: str
8
8
  data: Any = None
9
9
  command: str = 'select'
10
+
11
+ def __str__(self) -> str:
12
+ return self.name
@@ -1,18 +1,27 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from rich.text import Text
3
4
  from textual.widgets import Label
4
5
 
5
6
  from inquirer_textual.common.Choice import Choice
7
+ from inquirer_textual.common.StandardTheme import StandardTheme
6
8
 
7
9
 
8
10
  class ChoiceLabel(Label):
9
- def __init__(self, item: str | Choice):
10
- super().__init__(f' {item if isinstance(item, str) else item.name}')
11
- self._text = item if isinstance(item, str) else item.name
11
+ def __init__(self, item: str | Choice, pattern: str | None = None):
12
+ self._text = ChoiceLabel._get_text(item, pattern)
13
+ super().__init__(Text(' ').append_text(self._text))
12
14
  self.item = item
13
15
 
16
+ @classmethod
17
+ def _get_text(cls, item: str | Choice, pattern: str | None = None) -> Text:
18
+ result = Text(item if isinstance(item, str) else item.name)
19
+ if pattern:
20
+ result.highlight_words([pattern], style=StandardTheme.prompt_color, case_sensitive=False)
21
+ return result
22
+
14
23
  def add_pointer(self):
15
- self.update(f'\u276f {self._text}')
24
+ self.update(Text(f'{StandardTheme.pointer_character} ').append_text(self._text))
16
25
 
17
26
  def remove_pointer(self):
18
- self.update(f' {self._text}')
27
+ self.update(Text(' ').append_text(self._text))
@@ -0,0 +1,24 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import dataclass
5
+ from typing import TypeVar, Generic
6
+
7
+ T = TypeVar('T')
8
+
9
+
10
+ @dataclass
11
+ class InquirerResult(Generic[T]):
12
+ name: str | None
13
+ value: T
14
+ command: str | None
15
+
16
+ def json(self):
17
+ d = {}
18
+ if self.name:
19
+ d[self.name] = self.value
20
+ else:
21
+ d['value'] = self.value
22
+ if self.command:
23
+ d['command'] = self.command
24
+ return json.dumps(d)
@@ -0,0 +1,10 @@
1
+ class StandardTheme:
2
+ input_color = '#98c379'
3
+ prompt_color = '#c678dd'
4
+ error_color = '#e06c75'
5
+ pointer_character = '\u276f'
6
+ select_list_item_highlight_foreground = '#61afef'
7
+ select_question_mark = '#e5c07b'
8
+
9
+
10
+
@@ -4,73 +4,85 @@ from textual.validation import Validator
4
4
 
5
5
  from inquirer_textual.InquirerApp import InquirerApp
6
6
  from inquirer_textual.common.Choice import Choice
7
- from inquirer_textual.common.Result import Result
8
- from inquirer_textual.common.Shortcut import Shortcut
9
7
  from inquirer_textual.widgets.InquirerCheckbox import InquirerCheckbox
10
8
  from inquirer_textual.widgets.InquirerConfirm import InquirerConfirm
9
+ from inquirer_textual.widgets.InquirerEditor import InquirerEditor
11
10
  from inquirer_textual.widgets.InquirerMulti import InquirerMulti
12
11
  from inquirer_textual.widgets.InquirerNumber import InquirerNumber
12
+ from inquirer_textual.widgets.InquirerPath import InquirerPath, PathType
13
+ from inquirer_textual.widgets.InquirerPattern import InquirerPattern
13
14
  from inquirer_textual.widgets.InquirerSecret import InquirerSecret
14
15
  from inquirer_textual.widgets.InquirerSelect import InquirerSelect
15
16
  from inquirer_textual.widgets.InquirerText import InquirerText
16
17
  from inquirer_textual.widgets.InquirerWidget import InquirerWidget
17
18
 
18
19
 
19
- def text(message: str, shortcuts: list[Shortcut] | None = None,
20
- validators: Validator | Iterable[Validator] | None = None) -> Result[str]:
21
- app: InquirerApp[str] = InquirerApp()
22
- app.widget = InquirerText(message, validators=validators)
23
- app.shortcuts = shortcuts
24
- app.show_footer = bool(shortcuts)
25
- return app.run(inline=True)
20
+ def checkbox(message: str, choices: list[str | Choice], enabled: list[str | Choice] | None = None,
21
+ mandatory: bool = False, clear: bool = False) -> list[str | Choice]:
22
+ app: InquirerApp[list[str | Choice]] = InquirerApp()
23
+ app.widget = InquirerCheckbox(message, choices, enabled=enabled, mandatory=mandatory)
24
+ return app.run(inline=True, inline_no_clear=not clear).value
25
+
26
+
27
+ def confirm(message: str, default: bool = False, mandatory: bool = False, clear: bool = False) -> bool:
28
+ app: InquirerApp[bool] = InquirerApp()
29
+ app.widget = InquirerConfirm(message, default=default, mandatory=mandatory)
30
+ return app.run(inline=True, inline_no_clear=not clear).value
26
31
 
27
32
 
28
- def secret(message: str, shortcuts: list[Shortcut] | None = None) -> Result[str]:
33
+ def editor(message: str, clear: bool = False) -> str:
29
34
  app: InquirerApp[str] = InquirerApp()
30
- app.widget = InquirerSecret(message)
31
- app.shortcuts = shortcuts
32
- app.show_footer = bool(shortcuts)
33
- return app.run(inline=True)
35
+ app.widget = InquirerEditor(message)
36
+ return app.run(inline=True, inline_no_clear=not clear).value
37
+
38
+
39
+ def external(widget: InquirerWidget, clear: bool = False) -> Any:
40
+ app: InquirerApp[Any] = InquirerApp()
41
+ app.widget = widget
42
+ return app.run(inline=True, inline_no_clear=not clear).value
43
+
44
+
45
+ def multi(widgets: list[tuple[str, InquirerWidget]], clear: bool = False) -> dict[str, Any]:
46
+ app: InquirerApp[dict[str, Any]] = InquirerApp()
47
+ app.widget = InquirerMulti(widgets)
48
+ return app.run(inline=True, inline_no_clear=not clear).value
34
49
 
35
50
 
36
- def number(message: str, shortcuts: list[Shortcut] | None = None) -> Result[int]:
51
+ def number(message: str, mandatory: bool = False, clear: bool = False) -> int:
37
52
  app: InquirerApp[int] = InquirerApp()
38
- app.widget = InquirerNumber(message)
39
- app.shortcuts = shortcuts
40
- app.show_footer = bool(shortcuts)
41
- return app.run(inline=True)
53
+ app.widget = InquirerNumber(message, mandatory=mandatory)
54
+ return app.run(inline=True, inline_no_clear=not clear).value
42
55
 
43
56
 
44
- def confirm(message: str, shortcuts: list[Shortcut] | None = None, default: bool = False, mandatory: bool = True) -> \
45
- Result[bool]:
46
- app: InquirerApp[bool] = InquirerApp()
47
- app.widget = InquirerConfirm(message, default=default, mandatory=mandatory)
48
- app.shortcuts = shortcuts
49
- app.show_footer = bool(shortcuts)
50
- return app.run(inline=True)
57
+ def path(message: str, exists: bool = False, path_type: PathType = PathType.ANY, mandatory: bool = False,
58
+ clear: bool = False) -> str:
59
+ app: InquirerApp[str] = InquirerApp()
60
+ app.widget = InquirerPath(message, exists=exists, path_type=path_type, mandatory=mandatory)
61
+ return app.run(inline=True, inline_no_clear=not clear).value
51
62
 
52
63
 
53
- def select(message: str, choices: list[str | Choice], shortcuts: list[Shortcut] | None = None,
54
- default: str | Choice | None = None, mandatory: bool = True) -> Result[str | Choice]:
64
+ def pattern(message: str, choices: list[str | Choice], default: str | Choice | None = None,
65
+ mandatory: bool = False, clear: bool = False) -> str | Choice:
55
66
  app: InquirerApp[str | Choice] = InquirerApp()
56
- app.widget = InquirerSelect(message, choices, default, mandatory)
57
- app.shortcuts = shortcuts
58
- app.show_footer = bool(shortcuts)
59
- return app.run(inline=True)
67
+ app.widget = InquirerPattern(message, choices, default=default, mandatory=mandatory)
68
+ return app.run(inline=True, inline_no_clear=not clear).value
60
69
 
61
70
 
62
- def checkbox(message: str, choices: list[str | Choice], shortcuts: list[Shortcut] | None = None,
63
- enabled: list[str | Choice] | None = None) -> Result[list[str | Choice]]:
64
- app: InquirerApp[list[str | Choice]] = InquirerApp()
65
- app.widget = InquirerCheckbox(message, choices, enabled)
66
- app.shortcuts = shortcuts
67
- app.show_footer = bool(shortcuts)
68
- return app.run(inline=True)
71
+ def secret(message: str, mandatory: bool = False, clear: bool = False) -> str:
72
+ app: InquirerApp[str] = InquirerApp()
73
+ app.widget = InquirerSecret(message, mandatory=mandatory)
74
+ return app.run(inline=True, inline_no_clear=not clear).value
69
75
 
70
76
 
71
- def multi(widgets: list[InquirerWidget], shortcuts: list[Shortcut] | None = None) -> Result[list[Any]]:
72
- app: InquirerApp[list[Any]] = InquirerApp()
73
- app.widget = InquirerMulti(widgets)
74
- app.shortcuts = shortcuts
75
- app.show_footer = bool(shortcuts)
76
- return app.run(inline=True)
77
+ def select(message: str, choices: list[str | Choice], default: str | Choice | None = None,
78
+ mandatory: bool = False, clear: bool = False) -> str | Choice:
79
+ app: InquirerApp[str | Choice] = InquirerApp()
80
+ app.widget = InquirerSelect(message, choices, default=default, mandatory=mandatory)
81
+ return app.run(inline=True, inline_no_clear=not clear).value
82
+
83
+
84
+ def text(message: str, default: str = '', validators: Validator | Iterable[Validator] | None = None,
85
+ mandatory: bool = False, clear: bool = False) -> str:
86
+ app: InquirerApp[str] = InquirerApp()
87
+ app.widget = InquirerText(message, default=default, validators=validators, mandatory=mandatory)
88
+ return app.run(inline=True, inline_no_clear=not clear).value
@@ -1 +1 @@
1
- version = "0.2.0"
1
+ version = "0.3.0"
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from textual.app import ComposeResult
4
- from textual.containers import VerticalGroup
5
- from textual.widgets import ListItem, ListView
4
+ from textual.containers import VerticalGroup, HorizontalGroup
5
+ from textual.widgets import ListItem, ListView, Static
6
6
  from typing_extensions import Self
7
7
 
8
8
  from inquirer_textual.common.Choice import Choice
@@ -27,20 +27,25 @@ class InquirerCheckbox(InquirerWidget):
27
27
  ("space", "toggle_selected", "Toggle selection"),
28
28
  ]
29
29
 
30
- def __init__(self, message: str, choices: list[str | Choice], enabled: list[str | Choice] | None = None):
30
+ def __init__(self, message: str, choices: list[str | Choice], name: str | None = None,
31
+ enabled: list[str | Choice] | None = None, mandatory: bool = False):
31
32
  """
32
33
  Args:
33
34
  message (str): The prompt message to display.
34
35
  choices (list[str | Choice]): A list of choices to present to the user.
36
+ name (str | None): The name of the input field.
35
37
  enabled (list[str | Choice] | None): A list of choices that should be pre-selected.
38
+ mandatory (bool): Whether at least one selection is mandatory.
36
39
  """
37
- super().__init__()
40
+ super().__init__(name=name, mandatory=mandatory)
38
41
  self.message = message
39
42
  self.choices = choices
40
43
  self.enabled = enabled
41
44
  self.list_view: ListView | None = None
42
45
  self.selected_label: ChoiceCheckboxLabel | None = None
43
46
  self.selected_item: str | Choice | None = None
47
+ self.selected_value: list[str | Choice] | None = None
48
+ self.show_selected_value: bool = False
44
49
 
45
50
  def on_mount(self):
46
51
  self.styles.height = min(10, len(self.choices) + 1)
@@ -55,7 +60,7 @@ class InquirerCheckbox(InquirerWidget):
55
60
  self.selected_item = label.item
56
61
 
57
62
  def on_list_view_selected(self, _: ListView.Selected) -> None:
58
- self.post_message(InquirerWidget.Submit(self.current_value()))
63
+ self.submit_current_value()
59
64
 
60
65
  def focus(self, scroll_visible: bool = True) -> Self:
61
66
  if self.list_view:
@@ -67,12 +72,23 @@ class InquirerCheckbox(InquirerWidget):
67
72
  labels = self.query(ChoiceCheckboxLabel)
68
73
  return [label.item for label in labels if label.checked]
69
74
 
75
+ async def set_selected_value(self, value: list[str | Choice]) -> None:
76
+ self.selected_value = value
77
+ self.styles.height = 1
78
+ self.show_selected_value = True
79
+ await self.recompose()
80
+
70
81
  def compose(self) -> ComposeResult:
71
- with VerticalGroup():
72
- items: list[ListItem] = []
73
- for idx, choice in enumerate(self.choices):
74
- list_item = ListItem(ChoiceCheckboxLabel(choice))
75
- items.append(list_item)
76
- self.list_view = ListView(*items, id='inquirer-checkbox-list-view')
77
- yield PromptMessage(self.message)
78
- yield self.list_view
82
+ if self.show_selected_value:
83
+ with HorizontalGroup():
84
+ yield PromptMessage(self.message)
85
+ yield Static(str(self.selected_value))
86
+ else:
87
+ with VerticalGroup():
88
+ items: list[ListItem] = []
89
+ for idx, choice in enumerate(self.choices):
90
+ list_item = ListItem(ChoiceCheckboxLabel(choice))
91
+ items.append(list_item)
92
+ self.list_view = ListView(*items, id='inquirer-checkbox-list-view')
93
+ yield PromptMessage(self.message)
94
+ yield self.list_view
@@ -1,10 +1,13 @@
1
- from inquirer_textual.widgets.InquirerWidget import InquirerWidget
2
- from inquirer_textual.common.PromptMessage import PromptMessage
1
+ from __future__ import annotations
2
+
3
3
  from textual import events
4
4
  from textual.app import ComposeResult
5
5
  from textual.containers import HorizontalGroup
6
6
  from textual.widgets import Label
7
7
 
8
+ from inquirer_textual.common.PromptMessage import PromptMessage
9
+ from inquirer_textual.widgets.InquirerWidget import InquirerWidget
10
+
8
11
 
9
12
  class InquirerConfirm(InquirerWidget):
10
13
  """A confirmation prompt that allows the user to confirm or reject."""
@@ -16,17 +19,18 @@ class InquirerConfirm(InquirerWidget):
16
19
  """
17
20
  can_focus = True
18
21
 
19
- def __init__(self, message: str, confirm_character: str = 'y', reject_character: str = 'n', default=False,
20
- mandatory: bool = True):
22
+ def __init__(self, message: str, confirm_character: str = 'y', reject_character: str = 'n', name: str | None = None,
23
+ default=False, mandatory: bool = False):
21
24
  """
22
25
  Args:
23
26
  message (str): The prompt message to display.
24
27
  confirm_character (str): The character to use for confirmation.
25
28
  reject_character (str): The character to use for rejection.
29
+ name (str | None): The name of the prompt.
26
30
  default (bool): The default value if the user presses Enter without input.
27
31
  mandatory (bool): Whether a response is mandatory.
28
32
  """
29
- super().__init__(mandatory)
33
+ super().__init__(name=name, mandatory=mandatory)
30
34
  if len(confirm_character) != 1 or len(reject_character) != 1:
31
35
  raise ValueError("confirm_character and reject_character must be a single character")
32
36
  if confirm_character.lower() == reject_character.lower():
@@ -0,0 +1,72 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import tempfile
5
+
6
+ from textual import events
7
+ from textual.app import ComposeResult
8
+ from textual.containers import HorizontalGroup
9
+ from textual.widgets import Static
10
+
11
+ from inquirer_textual.common.PromptMessage import PromptMessage
12
+ from inquirer_textual.widgets.InquirerWidget import InquirerWidget
13
+
14
+
15
+ class InquirerEditor(InquirerWidget):
16
+ """An input widget that uses an external editor."""
17
+
18
+ DEFAULT_CSS = """
19
+ InquirerEditor {
20
+ height: auto;
21
+ }
22
+ """
23
+ can_focus = True
24
+
25
+ def __init__(self, message: str | None = None, name: str | None = None):
26
+ """
27
+ Args:
28
+ message (str): The prompt message to display.
29
+ name (str): The name of the prompt.
30
+ """
31
+ super().__init__(name=name)
32
+ self.message = message
33
+
34
+ def on_mount(self):
35
+ super().on_mount()
36
+ if not self.message:
37
+ self._launch_editor()
38
+
39
+ def on_key(self, event: events.Key):
40
+ if event.key == 'enter':
41
+ event.stop()
42
+ self._launch_editor()
43
+
44
+ def _launch_editor(self):
45
+ with self.app.suspend():
46
+ tmp = tempfile.NamedTemporaryFile()
47
+ filename = tmp.name
48
+ os.system(f"{get_editor_command()} {filename}")
49
+ with open(filename, 'r') as f:
50
+ content = f.read()
51
+ os.unlink(filename)
52
+ self.post_message(InquirerWidget.Submit(content))
53
+
54
+ def compose(self) -> ComposeResult:
55
+ if self.message:
56
+ with HorizontalGroup():
57
+ yield PromptMessage(self.message)
58
+ yield Static('[dim]Press <enter> to launch editor[/dim]')
59
+ else:
60
+ super().compose()
61
+
62
+
63
+ def get_editor_command() -> str:
64
+ editor = os.environ.get('VISUAL')
65
+ if not editor:
66
+ editor = os.environ.get('EDITOR')
67
+ if editor == 'nano':
68
+ return 'nano -R'
69
+ elif editor == 'code':
70
+ return 'code -w -n'
71
+ else:
72
+ return 'vim -f -o'
@@ -15,7 +15,7 @@ class InquirerMulti(InquirerWidget):
15
15
  }
16
16
  """
17
17
 
18
- def __init__(self, widgets: list[InquirerWidget]) -> None:
18
+ def __init__(self, widgets: list[tuple[str, InquirerWidget]]) -> None:
19
19
  """
20
20
  Args:
21
21
  widgets (list[InquirerWidget]): A list of InquirerWidget instances to present in sequence.
@@ -23,24 +23,25 @@ class InquirerMulti(InquirerWidget):
23
23
  super().__init__()
24
24
  self.widgets = widgets
25
25
  self._current_widget_index = 0
26
- self._return_values: list[Any] = []
26
+ self._return_values_dict: dict[str, Any] = {}
27
27
 
28
28
  def on_mount(self):
29
29
  self.query_one(ContentSwitcher).current = f'widget-{self._current_widget_index}'
30
30
  self.query_one(ContentSwitcher).visible_content.focus()
31
31
 
32
32
  def on_inquirer_widget_submit(self, message: InquirerWidget.Submit) -> None:
33
- self._return_values.append(message.value)
33
+ current_widget = self.widgets[self._current_widget_index]
34
+ self._return_values_dict[current_widget[0]] = message.value
34
35
  self._current_widget_index += 1
35
36
  if self._current_widget_index < len(self.widgets):
36
37
  message.stop()
37
38
  self.query_one(ContentSwitcher).current = f'widget-{self._current_widget_index}'
38
39
  self.query_one(ContentSwitcher).visible_content.focus()
39
40
  else:
40
- message.value = self._return_values
41
+ message.value = self._return_values_dict
41
42
 
42
43
  def compose(self) -> ComposeResult:
43
44
  with ContentSwitcher(initial=f'widget-{self._current_widget_index}'):
44
45
  for idx, widget in enumerate(self.widgets):
45
- widget.id = f'widget-{idx}'
46
- yield widget
46
+ widget[1].id = f'widget-{idx}'
47
+ yield widget[1]
@@ -1,13 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing_extensions import Self
4
-
5
3
  from textual.app import ComposeResult
6
4
  from textual.containers import HorizontalGroup
7
5
  from textual.widgets import Input
6
+ from typing_extensions import Self, Literal
8
7
 
9
- from inquirer_textual.widgets.InquirerWidget import InquirerWidget
10
8
  from inquirer_textual.common.PromptMessage import PromptMessage
9
+ from inquirer_textual.widgets.InquirerWidget import InquirerWidget
11
10
 
12
11
 
13
12
  class InquirerNumber(InquirerWidget):
@@ -26,17 +25,22 @@ class InquirerNumber(InquirerWidget):
26
25
  }
27
26
  """
28
27
 
29
- def __init__(self, message: str):
28
+ def __init__(self, message: str, name: str | None = None, input_type: Literal['integer', 'number'] = 'integer',
29
+ mandatory: bool = False):
30
30
  """
31
31
  Args:
32
32
  message (str): The prompt message to display.
33
+ name (str | None): The name of the input field.
34
+ input_type (Literal['integer', 'number']): The type of number input ('integer' or 'number').
35
+ mandatory (bool): Whether the input is mandatory.
33
36
  """
34
- super().__init__()
37
+ super().__init__(name=name, mandatory=mandatory)
35
38
  self.message = message
39
+ self.input_type = input_type
36
40
  self.input: Input | None = None
37
41
 
38
- def on_input_submitted(self, submitted: Input.Submitted):
39
- self.post_message(InquirerWidget.Submit(submitted.value))
42
+ def on_input_submitted(self) -> None:
43
+ self.submit_current_value()
40
44
 
41
45
  def focus(self, scroll_visible: bool = True) -> Self:
42
46
  if self.input:
@@ -45,10 +49,15 @@ class InquirerNumber(InquirerWidget):
45
49
  return super().focus(scroll_visible)
46
50
 
47
51
  def current_value(self):
48
- return self.input.value if self.input else None
52
+ if self.input and self.input.value:
53
+ if self.input_type == 'integer':
54
+ return int(self.input.value)
55
+ elif self.input_type == 'number':
56
+ return float(self.input.value)
57
+ return None
49
58
 
50
59
  def compose(self) -> ComposeResult:
51
60
  with HorizontalGroup():
52
61
  yield PromptMessage(self.message)
53
- self.input = Input(id="inquirer-number-input", type="integer")
62
+ self.input = Input(id="inquirer-number-input", type=self.input_type)
54
63
  yield self.input
@@ -0,0 +1,93 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+ from pathlib import Path
5
+
6
+ from textual.app import ComposeResult
7
+ from textual.containers import HorizontalGroup
8
+ from textual.widgets import Input, Static
9
+ from typing_extensions import Self
10
+
11
+ from inquirer_textual.common.PromptMessage import PromptMessage
12
+ from inquirer_textual.widgets.InquirerWidget import InquirerWidget
13
+
14
+
15
+ class PathType(Enum):
16
+ FILE = 'file'
17
+ DIRECTORY = 'directory'
18
+ ANY = 'any'
19
+
20
+
21
+ class InquirerPath(InquirerWidget):
22
+ """An input prompt that allows the user to enter a file path."""
23
+
24
+ DEFAULT_CSS = """
25
+ InquirerPath {
26
+ height: auto;
27
+ }
28
+ #inquirer-path-input {
29
+ border: none;
30
+ background: transparent;
31
+ color: $input-color;
32
+ padding: 0;
33
+ height: 1;
34
+ }
35
+ #inquirer-path-error-message {
36
+ color: $error-color;
37
+ height: auto;
38
+ }
39
+ """
40
+
41
+ def __init__(self, message: str, name: str | None = None, default: str = '', exists: bool = False,
42
+ path_type: PathType = PathType.ANY, mandatory: bool = False):
43
+ """
44
+ Args:
45
+ message (str): The prompt message to display.
46
+ default (str): The default value if the user presses Enter without input.
47
+ exists (bool): If True, validate that the entered path exists.
48
+ """
49
+ super().__init__(name=name, mandatory=mandatory)
50
+ self.message = message
51
+ self.input: Input | None = None
52
+ self.default = default
53
+ self.exists = exists
54
+ self.path_type = path_type
55
+
56
+ def on_mount(self):
57
+ super().on_mount()
58
+ self.input.value = self.default
59
+
60
+ def on_input_submitted(self, submitted: Input.Submitted):
61
+ if self.exists:
62
+ if not Path(submitted.value).exists():
63
+ self._show_validation_error("The specified path does not exist.")
64
+ return
65
+ if self.path_type != PathType.ANY:
66
+ path = Path(submitted.value)
67
+ if self.path_type == PathType.FILE and not path.is_file():
68
+ self._show_validation_error("The specified path is not a file.")
69
+ return
70
+ if self.path_type == PathType.DIRECTORY and not path.is_dir():
71
+ self._show_validation_error("The specified path is not a directory.")
72
+ return
73
+ self.submit_current_value()
74
+
75
+ def _show_validation_error(self, message: str):
76
+ error_message = self.query_one('#inquirer-path-error-message', Static)
77
+ error_message.update(message)
78
+
79
+ def focus(self, scroll_visible: bool = True) -> Self:
80
+ if self.input:
81
+ return self.input.focus(scroll_visible)
82
+ else:
83
+ return super().focus(scroll_visible)
84
+
85
+ def current_value(self):
86
+ return self.input.value if self.input else None
87
+
88
+ def compose(self) -> ComposeResult:
89
+ with HorizontalGroup():
90
+ yield PromptMessage(self.message)
91
+ self.input = Input(id="inquirer-path-input")
92
+ yield self.input
93
+ yield Static("", id="inquirer-path-error-message")
@@ -0,0 +1,169 @@
1
+ from __future__ import annotations
2
+
3
+ from textual import on, events
4
+ from textual.app import ComposeResult
5
+ from textual.containers import VerticalGroup, HorizontalGroup
6
+ from textual.css.query import NoMatches
7
+ from textual.reactive import reactive
8
+ from textual.widgets import ListView, ListItem, Input, Static
9
+ from typing_extensions import Self
10
+
11
+ from inquirer_textual.common.Choice import Choice
12
+ from inquirer_textual.common.ChoiceLabel import ChoiceLabel
13
+ from inquirer_textual.common.PromptMessage import PromptMessage
14
+ from inquirer_textual.common.StandardTheme import StandardTheme
15
+ from inquirer_textual.widgets.InquirerWidget import InquirerWidget
16
+
17
+
18
+ class InquirerPattern(InquirerWidget):
19
+ """A select widget that allows a single selection from a list of choices with pattern filtering."""
20
+
21
+ DEFAULT_CSS = """
22
+ #inquirer-pattern-list-view {
23
+ background: transparent;
24
+ }
25
+ #inquirer-pattern-list-view ListItem.-highlight {
26
+ color: $select-list-item-highlight-foreground;
27
+ background: transparent;
28
+ }
29
+ #inquirer-pattern-query-container {
30
+ width: auto;
31
+ # border: red;
32
+ }
33
+ #inquirer-pattern-query {
34
+ border: none;
35
+ background: transparent;
36
+ color: $input-color;
37
+ padding: 0;
38
+ height: 1;
39
+ width: 20;
40
+ }
41
+ #inquirer-pattern-query-pointer {
42
+ width: auto;
43
+ color: $prompt-color;
44
+ }
45
+ """
46
+
47
+ candidates: reactive[list[str | Choice]] = reactive([])
48
+
49
+ def __init__(self, message: str, choices: list[str | Choice], name: str | None = None,
50
+ default: str | Choice | None = None, mandatory: bool = True):
51
+ """
52
+ Args:
53
+ message (str): The prompt message to display.
54
+ choices (list[str | Choice]): A list of choices to present to the user.
55
+ default (str | Choice | None): The default choice to pre-select.
56
+ mandatory (bool): Whether a response is mandatory.
57
+ """
58
+ super().__init__(name=name, mandatory=mandatory)
59
+ self.message = message
60
+ self.choices = choices
61
+ self.candidates = choices.copy()
62
+ self.list_view: ListView | None = None
63
+ self.selected_label: ChoiceLabel | None = None
64
+ self.selected_item: str | Choice | None = None
65
+ self.default = default
66
+ self.query: Input | None = None
67
+
68
+ def on_mount(self):
69
+ super().on_mount()
70
+ if self.app.is_inline:
71
+ self.styles.height = min(10, len(self.choices) + 1)
72
+ else:
73
+ self.styles.height = '1fr'
74
+
75
+ def on_list_view_highlighted(self, event: ListView.Highlighted) -> None:
76
+ if self.selected_label:
77
+ self.selected_label.remove_pointer()
78
+ if event.item:
79
+ try:
80
+ label = event.item.query_one(ChoiceLabel)
81
+ label.add_pointer()
82
+ self.selected_label = label
83
+ self.selected_item = label.item
84
+ return
85
+ except NoMatches:
86
+ pass
87
+ self.selected_label = None
88
+ self.selected_item = None
89
+
90
+ def on_list_view_selected(self, _: ListView.Selected):
91
+ if isinstance(self.selected_item, Choice):
92
+ self.submit_current_value(self.selected_item.command)
93
+ else:
94
+ self.submit_current_value()
95
+
96
+ def focus(self, scroll_visible: bool = True) -> Self:
97
+ if self.query:
98
+ return self.query.focus(scroll_visible)
99
+ else:
100
+ return super().focus(scroll_visible)
101
+
102
+ def current_value(self):
103
+ return self.selected_item
104
+
105
+ def _collect_list_items(self):
106
+ items: list[ListItem] = []
107
+ for candidate in self.candidates:
108
+ list_item = ListItem(ChoiceLabel(candidate, self.query.value if self.query else None))
109
+ items.append(list_item)
110
+ return items
111
+
112
+ def _find_initial_index(self):
113
+ initial_index = 0
114
+ for idx, choice in enumerate(self.choices):
115
+ if self.default and choice == self.default:
116
+ initial_index = idx
117
+ return initial_index
118
+
119
+ @on(Input.Changed, '#inquirer-pattern-query')
120
+ async def handle_query_changed(self, event: Input.Changed):
121
+ query = event.value.lower()
122
+ if query == '':
123
+ self.candidates = self.choices.copy()
124
+ else:
125
+ filtered = []
126
+ for choice in self.choices:
127
+ name = choice.name if isinstance(choice, Choice) else choice
128
+ if query in name.lower():
129
+ filtered.append(choice)
130
+ self.candidates = filtered
131
+ assert isinstance(self.list_view, ListView)
132
+ await self.list_view.clear()
133
+ list_items = self._collect_list_items()
134
+ await self.list_view.extend(list_items)
135
+ if list_items:
136
+ self.list_view.index = 0
137
+
138
+ def watch_candidates(self, candidates: list[str | Choice]) -> None:
139
+ count_suffix = f'[{len(candidates)}/{len(self.choices)}]'
140
+ try:
141
+ count_widget = self.query_one('#inquirer-pattern-query-count-suffix', Static)
142
+ count_widget.update(count_suffix)
143
+ except NoMatches:
144
+ pass
145
+
146
+ def on_key(self, event: events.Key):
147
+ assert isinstance(self.list_view, ListView)
148
+ if event.key == 'down':
149
+ event.stop()
150
+ self.list_view.action_cursor_down()
151
+ elif event.key == 'up':
152
+ event.stop()
153
+ self.list_view.action_cursor_up()
154
+ elif event.key == 'enter':
155
+ event.stop()
156
+ self.list_view.action_select_cursor()
157
+
158
+ def compose(self) -> ComposeResult:
159
+ with VerticalGroup():
160
+ self.list_view = ListView(*self._collect_list_items(), id='inquirer-pattern-list-view',
161
+ initial_index=self._find_initial_index())
162
+ with HorizontalGroup():
163
+ yield PromptMessage(self.message)
164
+ yield Static(f'[{len(self.candidates)}/{len(self.choices)}]', id='inquirer-pattern-query-count-suffix')
165
+ with HorizontalGroup(id='inquirer-pattern-query-container'):
166
+ yield Static(f'{StandardTheme.pointer_character} ', id='inquirer-pattern-query-pointer')
167
+ self.query = Input(id="inquirer-pattern-query")
168
+ yield self.query
169
+ yield self.list_view
@@ -25,12 +25,12 @@ class InquirerSecret(InquirerWidget):
25
25
  }
26
26
  """
27
27
 
28
- def __init__(self, message: str):
28
+ def __init__(self, message: str, name: str | None = None, mandatory: bool = False):
29
29
  """
30
30
  Args:
31
31
  message (str): The prompt message to display.
32
32
  """
33
- super().__init__()
33
+ super().__init__(name=name, mandatory=mandatory)
34
34
  self.message = message
35
35
  self.input: Input | None = None
36
36
 
@@ -24,8 +24,8 @@ class InquirerSelect(InquirerWidget):
24
24
  }
25
25
  """
26
26
 
27
- def __init__(self, message: str, choices: list[str | Choice], default: str | Choice | None = None,
28
- mandatory: bool = True):
27
+ def __init__(self, message: str, choices: list[str | Choice], name: str | None = None,
28
+ default: str | Choice | None = None, mandatory: bool = True):
29
29
  """
30
30
  Args:
31
31
  message (str): The prompt message to display.
@@ -33,7 +33,7 @@ class InquirerSelect(InquirerWidget):
33
33
  default (str | Choice | None): The default choice to pre-select.
34
34
  mandatory (bool): Whether a response is mandatory.
35
35
  """
36
- super().__init__(mandatory)
36
+ super().__init__(name=name, mandatory=mandatory)
37
37
  self.message = message
38
38
  self.choices = choices
39
39
  self.list_view: ListView | None = None
@@ -58,9 +58,9 @@ class InquirerSelect(InquirerWidget):
58
58
 
59
59
  def on_list_view_selected(self, _: ListView.Selected):
60
60
  if isinstance(self.selected_item, Choice):
61
- self.post_message(InquirerWidget.Submit(self.selected_item, self.selected_item.command))
61
+ self.submit_current_value(self.selected_item.command)
62
62
  else:
63
- self.post_message(InquirerWidget.Submit(self.selected_item))
63
+ self.submit_current_value()
64
64
 
65
65
  def focus(self, scroll_visible: bool = True) -> Self:
66
66
  if self.list_view:
@@ -28,7 +28,9 @@ class InquirerText(InquirerWidget):
28
28
  }
29
29
  """
30
30
 
31
- def __init__(self, message: str, default: str = '', validators: Validator | Iterable[Validator] | None = None):
31
+ def __init__(self, message: str, name: str | None = None, default: str = '',
32
+ validators: Validator | Iterable[Validator] | None = None,
33
+ mandatory: bool = False):
32
34
  """
33
35
  Args:
34
36
  message (str): The prompt message to display.
@@ -36,7 +38,7 @@ class InquirerText(InquirerWidget):
36
38
  validators (Validator | Iterable[Validator] | None): A validator or list of validators to validate the
37
39
  input.
38
40
  """
39
- super().__init__()
41
+ super().__init__(name=name, mandatory=mandatory)
40
42
  self.message = message
41
43
  self.input: Input | None = None
42
44
  self.default = default
@@ -48,7 +50,9 @@ class InquirerText(InquirerWidget):
48
50
 
49
51
  def on_input_submitted(self, submitted: Input.Submitted):
50
52
  if self.validators is None or submitted.validation_result.is_valid:
51
- self.post_message(InquirerWidget.Submit(submitted.value))
53
+ if self.input:
54
+ self.input._cursor_visible = False
55
+ self.submit_current_value()
52
56
 
53
57
  def focus(self, scroll_visible: bool = True) -> Self:
54
58
  if self.input:
@@ -13,8 +13,8 @@ class InquirerWidget(Widget):
13
13
  self.value = value
14
14
  self.command = command
15
15
 
16
- def __init__(self, mandatory: bool = True):
17
- super().__init__()
16
+ def __init__(self, name: str | None = None, mandatory: bool = False):
17
+ super().__init__(name=name)
18
18
  self.mandatory = mandatory
19
19
 
20
20
  def on_mount(self):
@@ -26,3 +26,9 @@ class InquirerWidget(Widget):
26
26
 
27
27
  def current_value(self):
28
28
  raise NotImplementedError("Subclasses must implement current_value method")
29
+
30
+ async def set_selected_value(self, value: Any) -> None:
31
+ pass
32
+
33
+ def submit_current_value(self, command: str | None = "select"):
34
+ self.post_message(InquirerWidget.Submit(self.current_value(), command))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: inquirer-textual
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Inquirer based on Textual
5
5
  Project-URL: Changelog, https://github.com/robvanderleek/inquirer-textual/blob/master/CHANGELOG.md
6
6
  Project-URL: Documentation, https://robvanderleek.github.io/inquirer-textual/
@@ -9,7 +9,9 @@ Author-email: Rob van der Leek <robvanderleek@gmail.com>
9
9
  License-Expression: GPL-3.0-or-later
10
10
  License-File: LICENSE
11
11
  Requires-Python: <3.15,>=3.9
12
- Requires-Dist: textual>=6.7.1
12
+ Requires-Dist: textual>=7.1.0
13
+ Provides-Extra: examples
14
+ Requires-Dist: textual-slider>=0.2.0; extra == 'examples'
13
15
  Description-Content-Type: text/markdown
14
16
 
15
17
  # Inquirer-Textual
@@ -36,11 +38,20 @@ Description-Content-Type: text/markdown
36
38
 
37
39
  All terminal programs start small. Some stay small, and some become incredibly
38
40
  big. The goal of this Python library is to make user input simple for small
39
- programs, but also support a smooth transition to a comprehensive UI library as
41
+ programs, while enabling a smooth transition to a comprehensive UI library as
40
42
  your program grows.
41
43
 
42
44
  Read the [documentation here](https://robvanderleek.github.io/inquirer-textual/)
43
45
 
46
+ ## Installation
47
+
48
+ Create and activate a virtual environment (for example with
49
+ [uv](https://docs.astral.sh/uv/)), and then install this package:
50
+
51
+ ```shell
52
+ pip install inquirer-textual
53
+ ```
54
+
44
55
  ## Development
45
56
 
46
57
  Add this library as an editable local dependency to another project using `uv`:
@@ -48,3 +59,31 @@ Add this library as an editable local dependency to another project using `uv`:
48
59
  ```shell
49
60
  uv add --editable <path-to-inquirer-textual>
50
61
  ```
62
+
63
+ ### Textual console
64
+
65
+ 1. Open the Textual Development Console:
66
+
67
+ ```shell
68
+ uv run textual console
69
+ ```
70
+
71
+ 2. Run application in development mode:
72
+
73
+ ```shell
74
+ uv run textual run --dev examples/prompt_pattern.py
75
+ ```
76
+
77
+ ### Static documentation
78
+
79
+ Generating the static documentation:
80
+
81
+ ```shell
82
+ uv run mkdocs build
83
+ ```
84
+
85
+ Viewing the static documentation:
86
+
87
+ ```shell
88
+ uv run mkdocs serve
89
+ ```
@@ -0,0 +1,30 @@
1
+ inquirer_textual/InquirerApp.py,sha256=ohFn5KpnZmd0AQVNRC3Ll3EMrXn-nwmedReb8sBRRsY,5979
2
+ inquirer_textual/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ inquirer_textual/prompts.py,sha256=lEVWbel8GvqRCgxGcp5ki_tSNMgVD84eaqawwySqwr4,4236
4
+ inquirer_textual/version.py,sha256=EqIccytbgmIh3EOfv7QIiksdjJSlB-o2LbyXxRtoMGs,18
5
+ inquirer_textual/common/AppConfig.py,sha256=z4PN75ABDQBg8QpRrJrWwL_PD5KEdx2lV1mlp6PEm7I,187
6
+ inquirer_textual/common/Choice.py,sha256=_0EGDbl6NGGiuOqCmfOkkmRX9IP09KQbRRmAdjPxwYs,203
7
+ inquirer_textual/common/ChoiceCheckboxLabel.py,sha256=BZg5vLe6_qc0ipYUaonNfygSOnHyJuEVrB8mmM6tOjw,675
8
+ inquirer_textual/common/ChoiceLabel.py,sha256=23ZtQ4FKPD4EdiuRPtTYPB4TOcQmNGq1JfoOlGowYZg,970
9
+ inquirer_textual/common/InquirerHeader.py,sha256=txl-TRe3DKsOPcmDKy1fhfTa-YAURsjTYePDDHzcPH8,802
10
+ inquirer_textual/common/InquirerResult.py,sha256=2inNdfc2xUTCUFqAGazKJJv6YUQjjFzn-f3NTDxm1-M,481
11
+ inquirer_textual/common/PromptMessage.py,sha256=MCXdsE9j0XlqVKjWUhXB-9qZ24Lk7dA5QWRgUWTdF8o,831
12
+ inquirer_textual/common/Shortcut.py,sha256=9mzLVnphImnehNo1HHU_gPMv-vFi5nXsb1G2CAbpuEY,297
13
+ inquirer_textual/common/StandardTheme.py,sha256=4y0HWIqKDTl1xT9i3TdzRWcDo1oDjvI6Wbzt_XBz2jI,233
14
+ inquirer_textual/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ inquirer_textual/widgets/InquirerCheckbox.py,sha256=84DoTtTLBL61dxJRTq23KPRZYPMcYF6rfT2B41A1gNA,3785
16
+ inquirer_textual/widgets/InquirerConfirm.py,sha256=oJSssC8L7_fWUqBJ46KLxwXajo8ZRVAdTNuxgWq-zvY,2432
17
+ inquirer_textual/widgets/InquirerEditor.py,sha256=n6SfKcaE2sNz8r_jvc9FvCWRyM8bHJcGj8Z5bQkgdnw,1999
18
+ inquirer_textual/widgets/InquirerMulti.py,sha256=wgrngSbbmJ4aTDig0_RTopYH8brTM4jPNvAKL7UZpZs,1754
19
+ inquirer_textual/widgets/InquirerNumber.py,sha256=mS5h0vi0W3q0JJwQxZEjAfoH-ARLoxMdrcffy9ANCSY,2120
20
+ inquirer_textual/widgets/InquirerPath.py,sha256=qiKkoWMS8yOVyOA82oezbrSvpUU4xIZxkOJMRR59FmY,3091
21
+ inquirer_textual/widgets/InquirerPattern.py,sha256=uyN6PchvPKOvQ7sSwYHnmDaplJMZwAVc7qnG7P0zCVo,6428
22
+ inquirer_textual/widgets/InquirerSecret.py,sha256=K2gZ-KsSjYZD-xPQa9PVqtlvIiQ17_X0ujGMnfBhWmo,1666
23
+ inquirer_textual/widgets/InquirerSelect.py,sha256=IsCDwLXTbzKMfwd-jjBdeRY3_734QOjoaJrjbGy3lMU,3237
24
+ inquirer_textual/widgets/InquirerText.py,sha256=xc21nreIFH-8YkuQfanQPsiLbLATfHVK7ggtTuWYU7Y,2316
25
+ inquirer_textual/widgets/InquirerWidget.py,sha256=TUU68Bef4A-S1bomgR0uFiVcQbfudrH-Vp9_h3WJgDg,1060
26
+ inquirer_textual/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
+ inquirer_textual-0.3.0.dist-info/METADATA,sha256=xdacOD3xrAWACJBJkN_jhPT4UNfGCGFW7v-Jz1Fhaps,2410
28
+ inquirer_textual-0.3.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
29
+ inquirer_textual-0.3.0.dist-info/licenses/LICENSE,sha256=fJuRou64yfkof3ZFdeHcgk7K8pqxis6SBr-vtUEgBuA,1073
30
+ inquirer_textual-0.3.0.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from dataclasses import dataclass
4
- from typing import TypeVar, Generic
5
-
6
- T = TypeVar('T')
7
-
8
-
9
- @dataclass
10
- class Result(Generic[T]):
11
- command: str | None
12
- value: T
13
-
14
- def __str__(self):
15
- return str(self.value)
@@ -1,25 +0,0 @@
1
- inquirer_textual/InquirerApp.py,sha256=2lDgzimH0EXeRnPDlre97q24TPsgIE9mI-cJfhQ8hIk,5377
2
- inquirer_textual/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- inquirer_textual/prompts.py,sha256=NvaU1hgLe962iSHH3EtmLkbB0K5Vlav38mcHSiYXrOg,3186
4
- inquirer_textual/version.py,sha256=XQVhijSHeIVMVzY2S4fY9BKxV2XHSTr9VVHsYltGPvQ,18
5
- inquirer_textual/common/Choice.py,sha256=ZRggsayRKcNMDLRh12Zblol_pusE2keVEXlHkaw5RsE,147
6
- inquirer_textual/common/ChoiceCheckboxLabel.py,sha256=BZg5vLe6_qc0ipYUaonNfygSOnHyJuEVrB8mmM6tOjw,675
7
- inquirer_textual/common/ChoiceLabel.py,sha256=Wxt5IZUSHJsVEza1Uj19HZP7oD1uNvclY1UDbaNiOOM,504
8
- inquirer_textual/common/InquirerHeader.py,sha256=txl-TRe3DKsOPcmDKy1fhfTa-YAURsjTYePDDHzcPH8,802
9
- inquirer_textual/common/PromptMessage.py,sha256=MCXdsE9j0XlqVKjWUhXB-9qZ24Lk7dA5QWRgUWTdF8o,831
10
- inquirer_textual/common/Result.py,sha256=mC79hFPITwyXFrrxlHxiumdywKhqGpBM32KhxzJvm-E,255
11
- inquirer_textual/common/Shortcut.py,sha256=9mzLVnphImnehNo1HHU_gPMv-vFi5nXsb1G2CAbpuEY,297
12
- inquirer_textual/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- inquirer_textual/widgets/InquirerCheckbox.py,sha256=ytWNqRbJ46iItJxbKfFTHGVapFnJjF9DwG3XtKfRW0E,3008
14
- inquirer_textual/widgets/InquirerConfirm.py,sha256=RFxSnxj1NS_kqirWWu_VLm0C2Bf4h-3zewDJ_IAtgYs,2293
15
- inquirer_textual/widgets/InquirerMulti.py,sha256=WhjhEq2j9IL4PIT2kgvClXorazeii4Lxv1TdUDyC60c,1637
16
- inquirer_textual/widgets/InquirerNumber.py,sha256=05ZEXHVeZ-jzvn7Ts0sJkpkP6oTlgBAWcZGEyrDG0H8,1550
17
- inquirer_textual/widgets/InquirerSecret.py,sha256=XnMP77O8XWeHvqmttb7xj94a3pgX95a9pEgLvIehmBg,1586
18
- inquirer_textual/widgets/InquirerSelect.py,sha256=P0_7B3945I-O2HXzXpGO5HDIqD7poR6aa0yOX6a9CGM,3259
19
- inquirer_textual/widgets/InquirerText.py,sha256=6sQxS87pAooCOdZBcZbxWfUXxqQZA5VYv1ebLv7-3J8,2154
20
- inquirer_textual/widgets/InquirerWidget.py,sha256=56rcnnK5vqD66WKfmv8V_kZ4YG63Vkxz3pYUq18OJKo,802
21
- inquirer_textual/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- inquirer_textual-0.2.0.dist-info/METADATA,sha256=etLlsW92h4EEQbgExg2JUV0ZNBPEsyBKpdgl4A4o8sE,1768
23
- inquirer_textual-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
24
- inquirer_textual-0.2.0.dist-info/licenses/LICENSE,sha256=fJuRou64yfkof3ZFdeHcgk7K8pqxis6SBr-vtUEgBuA,1073
25
- inquirer_textual-0.2.0.dist-info/RECORD,,