inquirer-textual 0.4.0__py3-none-any.whl → 1.0.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.
- inquirer_textual/InquirerApp.py +22 -24
- inquirer_textual/common/Choice.py +0 -3
- inquirer_textual/common/ChoiceLabel.py +5 -14
- inquirer_textual/common/{Prompt.py → PromptMessage.py} +3 -3
- inquirer_textual/common/Result.py +15 -0
- inquirer_textual/prompts.py +45 -57
- inquirer_textual/version.py +1 -1
- inquirer_textual/widgets/InquirerCheckbox.py +22 -30
- inquirer_textual/widgets/InquirerConfirm.py +11 -31
- inquirer_textual/widgets/InquirerMulti.py +17 -16
- inquirer_textual/widgets/InquirerNumber.py +12 -20
- inquirer_textual/widgets/InquirerSecret.py +6 -5
- inquirer_textual/widgets/InquirerSelect.py +28 -32
- inquirer_textual/widgets/InquirerText.py +7 -10
- inquirer_textual/widgets/InquirerWidget.py +2 -8
- {inquirer_textual-0.4.0.dist-info → inquirer_textual-1.0.0.dist-info}/METADATA +3 -42
- inquirer_textual-1.0.0.dist-info/RECORD +25 -0
- inquirer_textual/common/Answer.py +0 -22
- inquirer_textual/common/InquirerResult.py +0 -24
- inquirer_textual/common/defaults.py +0 -24
- inquirer_textual/widgets/InquirerEditor.py +0 -72
- inquirer_textual/widgets/InquirerPath.py +0 -95
- inquirer_textual/widgets/InquirerPattern.py +0 -175
- inquirer_textual-0.4.0.dist-info/RECORD +0 -30
- {inquirer_textual-0.4.0.dist-info → inquirer_textual-1.0.0.dist-info}/WHEEL +0 -0
- {inquirer_textual-0.4.0.dist-info → inquirer_textual-1.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,7 +5,7 @@ from textual.containers import HorizontalGroup
|
|
|
5
5
|
from textual.widgets import Input
|
|
6
6
|
from typing_extensions import Self
|
|
7
7
|
|
|
8
|
-
from inquirer_textual.common.
|
|
8
|
+
from inquirer_textual.common.PromptMessage import PromptMessage
|
|
9
9
|
from inquirer_textual.widgets.InquirerWidget import InquirerWidget
|
|
10
10
|
|
|
11
11
|
|
|
@@ -18,18 +18,19 @@ class InquirerSecret(InquirerWidget):
|
|
|
18
18
|
}
|
|
19
19
|
#inquirer-secret-input {
|
|
20
20
|
border: none;
|
|
21
|
-
|
|
21
|
+
background: transparent;
|
|
22
|
+
color: $input-color;
|
|
22
23
|
padding: 0;
|
|
23
24
|
height: 1;
|
|
24
25
|
}
|
|
25
26
|
"""
|
|
26
27
|
|
|
27
|
-
def __init__(self, message: str
|
|
28
|
+
def __init__(self, message: str):
|
|
28
29
|
"""
|
|
29
30
|
Args:
|
|
30
31
|
message (str): The prompt message to display.
|
|
31
32
|
"""
|
|
32
|
-
super().__init__(
|
|
33
|
+
super().__init__()
|
|
33
34
|
self.message = message
|
|
34
35
|
self.input: Input | None = None
|
|
35
36
|
|
|
@@ -47,7 +48,7 @@ class InquirerSecret(InquirerWidget):
|
|
|
47
48
|
|
|
48
49
|
def compose(self) -> ComposeResult:
|
|
49
50
|
with HorizontalGroup():
|
|
50
|
-
yield
|
|
51
|
+
yield PromptMessage(self.message)
|
|
51
52
|
self.input = Input(id="inquirer-secret-input")
|
|
52
53
|
self.input.password = True
|
|
53
54
|
yield self.input
|
|
@@ -1,22 +1,31 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from textual.app import ComposeResult
|
|
4
|
-
from textual.containers import VerticalGroup
|
|
4
|
+
from textual.containers import VerticalGroup
|
|
5
5
|
from textual.widgets import ListView, ListItem
|
|
6
6
|
from typing_extensions import Self
|
|
7
7
|
|
|
8
|
-
from inquirer_textual.common.Answer import Answer
|
|
9
8
|
from inquirer_textual.common.Choice import Choice
|
|
10
9
|
from inquirer_textual.common.ChoiceLabel import ChoiceLabel
|
|
11
|
-
from inquirer_textual.common.
|
|
10
|
+
from inquirer_textual.common.PromptMessage import PromptMessage
|
|
12
11
|
from inquirer_textual.widgets.InquirerWidget import InquirerWidget
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
class InquirerSelect(InquirerWidget):
|
|
16
15
|
"""A select widget that allows a single selection from a list of choices."""
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
DEFAULT_CSS = """
|
|
18
|
+
#inquirer-select-list-view {
|
|
19
|
+
background: transparent;
|
|
20
|
+
}
|
|
21
|
+
#inquirer-select-list-view ListItem.-highlight {
|
|
22
|
+
color: $select-list-item-highlight-foreground;
|
|
23
|
+
background: transparent;
|
|
24
|
+
}
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, message: str, choices: list[str | Choice], default: str | Choice | None = None,
|
|
28
|
+
mandatory: bool = True):
|
|
20
29
|
"""
|
|
21
30
|
Args:
|
|
22
31
|
message (str): The prompt message to display.
|
|
@@ -24,15 +33,13 @@ class InquirerSelect(InquirerWidget):
|
|
|
24
33
|
default (str | Choice | None): The default choice to pre-select.
|
|
25
34
|
mandatory (bool): Whether a response is mandatory.
|
|
26
35
|
"""
|
|
27
|
-
super().__init__(
|
|
36
|
+
super().__init__(mandatory)
|
|
28
37
|
self.message = message
|
|
29
38
|
self.choices = choices
|
|
30
39
|
self.list_view: ListView | None = None
|
|
31
40
|
self.selected_label: ChoiceLabel | None = None
|
|
32
41
|
self.selected_item: str | Choice | None = None
|
|
33
42
|
self.default = default
|
|
34
|
-
self.selected_value: str | Choice | None = None
|
|
35
|
-
self.show_selected_value: bool = False
|
|
36
43
|
|
|
37
44
|
def on_mount(self):
|
|
38
45
|
super().on_mount()
|
|
@@ -51,9 +58,9 @@ class InquirerSelect(InquirerWidget):
|
|
|
51
58
|
|
|
52
59
|
def on_list_view_selected(self, _: ListView.Selected):
|
|
53
60
|
if isinstance(self.selected_item, Choice):
|
|
54
|
-
self.
|
|
61
|
+
self.post_message(InquirerWidget.Submit(self.selected_item, self.selected_item.command))
|
|
55
62
|
else:
|
|
56
|
-
self.
|
|
63
|
+
self.post_message(InquirerWidget.Submit(self.selected_item))
|
|
57
64
|
|
|
58
65
|
def focus(self, scroll_visible: bool = True) -> Self:
|
|
59
66
|
if self.list_view:
|
|
@@ -64,26 +71,15 @@ class InquirerSelect(InquirerWidget):
|
|
|
64
71
|
def current_value(self):
|
|
65
72
|
return self.selected_item
|
|
66
73
|
|
|
67
|
-
async def set_selected_value(self, value: str | Choice) -> None:
|
|
68
|
-
self.selected_value = value
|
|
69
|
-
self.styles.height = 1
|
|
70
|
-
self.show_selected_value = True
|
|
71
|
-
await self.recompose()
|
|
72
|
-
|
|
73
74
|
def compose(self) -> ComposeResult:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if self.default and choice == self.default:
|
|
86
|
-
initial_index = idx
|
|
87
|
-
self.list_view = ListView(*items, id='inquirer-select-list-view', initial_index=initial_index)
|
|
88
|
-
yield Prompt(self.message)
|
|
89
|
-
yield self.list_view
|
|
75
|
+
with VerticalGroup():
|
|
76
|
+
initial_index = 0
|
|
77
|
+
items: list[ListItem] = []
|
|
78
|
+
for idx, choice in enumerate(self.choices):
|
|
79
|
+
list_item = ListItem(ChoiceLabel(choice))
|
|
80
|
+
items.append(list_item)
|
|
81
|
+
if self.default and choice == self.default:
|
|
82
|
+
initial_index = idx
|
|
83
|
+
self.list_view = ListView(*items, id='inquirer-select-list-view', initial_index=initial_index)
|
|
84
|
+
yield PromptMessage(self.message)
|
|
85
|
+
yield self.list_view
|
|
@@ -8,7 +8,7 @@ from textual.validation import Validator
|
|
|
8
8
|
from textual.widgets import Input
|
|
9
9
|
from typing_extensions import Self
|
|
10
10
|
|
|
11
|
-
from inquirer_textual.common.
|
|
11
|
+
from inquirer_textual.common.PromptMessage import PromptMessage
|
|
12
12
|
from inquirer_textual.widgets.InquirerWidget import InquirerWidget
|
|
13
13
|
|
|
14
14
|
|
|
@@ -21,15 +21,14 @@ class InquirerText(InquirerWidget):
|
|
|
21
21
|
}
|
|
22
22
|
#inquirer-text-input {
|
|
23
23
|
border: none;
|
|
24
|
-
|
|
24
|
+
background: transparent;
|
|
25
|
+
color: $input-color;
|
|
25
26
|
padding: 0;
|
|
26
27
|
height: 1;
|
|
27
28
|
}
|
|
28
29
|
"""
|
|
29
30
|
|
|
30
|
-
def __init__(self, message: str,
|
|
31
|
-
validators: Validator | Iterable[Validator] | None = None,
|
|
32
|
-
mandatory: bool = False):
|
|
31
|
+
def __init__(self, message: str, default: str = '', validators: Validator | Iterable[Validator] | None = None):
|
|
33
32
|
"""
|
|
34
33
|
Args:
|
|
35
34
|
message (str): The prompt message to display.
|
|
@@ -37,7 +36,7 @@ class InquirerText(InquirerWidget):
|
|
|
37
36
|
validators (Validator | Iterable[Validator] | None): A validator or list of validators to validate the
|
|
38
37
|
input.
|
|
39
38
|
"""
|
|
40
|
-
super().__init__(
|
|
39
|
+
super().__init__()
|
|
41
40
|
self.message = message
|
|
42
41
|
self.input: Input | None = None
|
|
43
42
|
self.default = default
|
|
@@ -49,9 +48,7 @@ class InquirerText(InquirerWidget):
|
|
|
49
48
|
|
|
50
49
|
def on_input_submitted(self, submitted: Input.Submitted):
|
|
51
50
|
if self.validators is None or submitted.validation_result.is_valid:
|
|
52
|
-
|
|
53
|
-
self.input._cursor_visible = False
|
|
54
|
-
self.submit_current_value()
|
|
51
|
+
self.post_message(InquirerWidget.Submit(submitted.value))
|
|
55
52
|
|
|
56
53
|
def focus(self, scroll_visible: bool = True) -> Self:
|
|
57
54
|
if self.input:
|
|
@@ -64,6 +61,6 @@ class InquirerText(InquirerWidget):
|
|
|
64
61
|
|
|
65
62
|
def compose(self) -> ComposeResult:
|
|
66
63
|
with HorizontalGroup():
|
|
67
|
-
yield
|
|
64
|
+
yield PromptMessage(self.message)
|
|
68
65
|
self.input = Input(id="inquirer-text-input", validators=self.validators)
|
|
69
66
|
yield 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,
|
|
17
|
-
super().__init__(
|
|
16
|
+
def __init__(self, mandatory: bool = True):
|
|
17
|
+
super().__init__()
|
|
18
18
|
self.mandatory = mandatory
|
|
19
19
|
|
|
20
20
|
def on_mount(self):
|
|
@@ -26,9 +26,3 @@ 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.
|
|
3
|
+
Version: 1.0.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,9 +9,7 @@ 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>=7.1
|
|
13
|
-
Provides-Extra: examples
|
|
14
|
-
Requires-Dist: textual-slider>=0.2.0; extra == 'examples'
|
|
12
|
+
Requires-Dist: textual>=6.7.1
|
|
15
13
|
Description-Content-Type: text/markdown
|
|
16
14
|
|
|
17
15
|
# Inquirer-Textual
|
|
@@ -38,20 +36,11 @@ Description-Content-Type: text/markdown
|
|
|
38
36
|
|
|
39
37
|
All terminal programs start small. Some stay small, and some become incredibly
|
|
40
38
|
big. The goal of this Python library is to make user input simple for small
|
|
41
|
-
programs,
|
|
39
|
+
programs, but also support a smooth transition to a comprehensive UI library as
|
|
42
40
|
your program grows.
|
|
43
41
|
|
|
44
42
|
Read the [documentation here](https://robvanderleek.github.io/inquirer-textual/)
|
|
45
43
|
|
|
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
|
-
|
|
55
44
|
## Development
|
|
56
45
|
|
|
57
46
|
Add this library as an editable local dependency to another project using `uv`:
|
|
@@ -59,31 +48,3 @@ Add this library as an editable local dependency to another project using `uv`:
|
|
|
59
48
|
```shell
|
|
60
49
|
uv add --editable <path-to-inquirer-textual>
|
|
61
50
|
```
|
|
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,25 @@
|
|
|
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=4se2-QRIPsQy0Qla6DZQFQ90RVImEWlZaaA4AT2r29U,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-1.0.0.dist-info/METADATA,sha256=M4X2f99JmAvvuAlK3FIJZCoL_DSgRN6BCF1wmPzbD0g,1768
|
|
23
|
+
inquirer_textual-1.0.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
24
|
+
inquirer_textual-1.0.0.dist-info/licenses/LICENSE,sha256=fJuRou64yfkof3ZFdeHcgk7K8pqxis6SBr-vtUEgBuA,1073
|
|
25
|
+
inquirer_textual-1.0.0.dist-info/RECORD,,
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
from rich.text import Text
|
|
2
|
-
from textual.app import ComposeResult
|
|
3
|
-
from textual.widget import Widget
|
|
4
|
-
from textual.widgets import Static
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class Answer(Widget):
|
|
8
|
-
DEFAULT_CSS = """
|
|
9
|
-
Answer {
|
|
10
|
-
height: auto;
|
|
11
|
-
}
|
|
12
|
-
#inquirer-textual-answer {
|
|
13
|
-
color: $inquirer-textual-input-color;
|
|
14
|
-
}
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
def __init__(self, text: str):
|
|
18
|
-
super().__init__()
|
|
19
|
-
self.text = text
|
|
20
|
-
|
|
21
|
-
def compose(self) -> ComposeResult:
|
|
22
|
-
yield Static(Text(self.text), id='inquirer-textual-answer')
|
|
@@ -1,24 +0,0 @@
|
|
|
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)
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
from textual.theme import Theme
|
|
2
|
-
|
|
3
|
-
DEFAULT_THEME = Theme(
|
|
4
|
-
name="inquirer-textual-default",
|
|
5
|
-
primary="#0178D4",
|
|
6
|
-
secondary="#004578",
|
|
7
|
-
accent="#c678dd",
|
|
8
|
-
warning="#ffa62b",
|
|
9
|
-
error="#e06c75",
|
|
10
|
-
success="#4EBF71",
|
|
11
|
-
foreground="#e0e0e0",
|
|
12
|
-
background="black",
|
|
13
|
-
surface="transparent",
|
|
14
|
-
variables={
|
|
15
|
-
'block-cursor-foreground': '#61afef',
|
|
16
|
-
'block-cursor-background': 'transparent',
|
|
17
|
-
'block-cursor-blurred-foreground': '#61afef',
|
|
18
|
-
'block-cursor-blurred-background': 'transparent',
|
|
19
|
-
'inquirer-textual-question-mark': '#e5c07b',
|
|
20
|
-
'inquirer-textual-input-color': '#98c379',
|
|
21
|
-
}
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
POINTER_CHARACTER = '\u276f'
|
|
@@ -1,72 +0,0 @@
|
|
|
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.Prompt import Prompt
|
|
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 Prompt(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'
|
|
@@ -1,95 +0,0 @@
|
|
|
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.Prompt import Prompt
|
|
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
|
-
color: $inquirer-textual-input-color;
|
|
31
|
-
padding: 0;
|
|
32
|
-
height: 1;
|
|
33
|
-
}
|
|
34
|
-
#inquirer-path-error-message {
|
|
35
|
-
color: $error;
|
|
36
|
-
height: auto;
|
|
37
|
-
}
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
def __init__(self, message: str, name: str | None = None, default: str = '', exists: bool = False,
|
|
41
|
-
path_type: PathType = PathType.ANY, mandatory: bool = False):
|
|
42
|
-
"""
|
|
43
|
-
Args:
|
|
44
|
-
message (str): The prompt message to display.
|
|
45
|
-
default (str): The default value if the user presses Enter without input.
|
|
46
|
-
exists (bool): If True, validate that the entered path exists.
|
|
47
|
-
"""
|
|
48
|
-
super().__init__(name=name, mandatory=mandatory)
|
|
49
|
-
self.message = message
|
|
50
|
-
self.input: Input | None = None
|
|
51
|
-
self.default = default
|
|
52
|
-
self.exists = exists
|
|
53
|
-
self.path_type = path_type
|
|
54
|
-
|
|
55
|
-
def on_mount(self):
|
|
56
|
-
super().on_mount()
|
|
57
|
-
self.input.value = self.default
|
|
58
|
-
|
|
59
|
-
def on_input_submitted(self, submitted: Input.Submitted):
|
|
60
|
-
if self.exists:
|
|
61
|
-
if not Path(submitted.value).exists():
|
|
62
|
-
self._show_validation_error("The specified path does not exist.")
|
|
63
|
-
return
|
|
64
|
-
if self.path_type != PathType.ANY:
|
|
65
|
-
path = Path(submitted.value)
|
|
66
|
-
if self.path_type == PathType.FILE and not path.is_file():
|
|
67
|
-
self._show_validation_error("The specified path is not a file.")
|
|
68
|
-
return
|
|
69
|
-
if self.path_type == PathType.DIRECTORY and not path.is_dir():
|
|
70
|
-
self._show_validation_error("The specified path is not a directory.")
|
|
71
|
-
return
|
|
72
|
-
self._show_validation_error('')
|
|
73
|
-
if self.input:
|
|
74
|
-
self.input._cursor_visible = False
|
|
75
|
-
self.submit_current_value()
|
|
76
|
-
|
|
77
|
-
def _show_validation_error(self, message: str):
|
|
78
|
-
error_message = self.query_one('#inquirer-path-error-message', Static)
|
|
79
|
-
error_message.update(message)
|
|
80
|
-
|
|
81
|
-
def focus(self, scroll_visible: bool = True) -> Self:
|
|
82
|
-
if self.input:
|
|
83
|
-
return self.input.focus(scroll_visible)
|
|
84
|
-
else:
|
|
85
|
-
return super().focus(scroll_visible)
|
|
86
|
-
|
|
87
|
-
def current_value(self):
|
|
88
|
-
return self.input.value if self.input else None
|
|
89
|
-
|
|
90
|
-
def compose(self) -> ComposeResult:
|
|
91
|
-
with HorizontalGroup():
|
|
92
|
-
yield Prompt(self.message)
|
|
93
|
-
self.input = Input(id="inquirer-path-input")
|
|
94
|
-
yield self.input
|
|
95
|
-
yield Static("", id="inquirer-path-error-message")
|