ferp 0.7.1__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.
Files changed (87) hide show
  1. ferp/__init__.py +3 -0
  2. ferp/__main__.py +4 -0
  3. ferp/__version__.py +1 -0
  4. ferp/app.py +9 -0
  5. ferp/cli.py +160 -0
  6. ferp/core/__init__.py +0 -0
  7. ferp/core/app.py +1312 -0
  8. ferp/core/bundle_installer.py +245 -0
  9. ferp/core/command_provider.py +77 -0
  10. ferp/core/dependency_manager.py +59 -0
  11. ferp/core/fs_controller.py +70 -0
  12. ferp/core/fs_watcher.py +144 -0
  13. ferp/core/messages.py +49 -0
  14. ferp/core/path_actions.py +124 -0
  15. ferp/core/paths.py +3 -0
  16. ferp/core/protocols.py +8 -0
  17. ferp/core/script_controller.py +515 -0
  18. ferp/core/script_protocol.py +35 -0
  19. ferp/core/script_runner.py +421 -0
  20. ferp/core/settings.py +16 -0
  21. ferp/core/settings_store.py +69 -0
  22. ferp/core/state.py +156 -0
  23. ferp/core/task_store.py +164 -0
  24. ferp/core/transcript_logger.py +95 -0
  25. ferp/domain/__init__.py +0 -0
  26. ferp/domain/scripts.py +29 -0
  27. ferp/fscp/host/__init__.py +11 -0
  28. ferp/fscp/host/host.py +439 -0
  29. ferp/fscp/host/managed_process.py +113 -0
  30. ferp/fscp/host/process_registry.py +124 -0
  31. ferp/fscp/protocol/__init__.py +13 -0
  32. ferp/fscp/protocol/errors.py +2 -0
  33. ferp/fscp/protocol/messages.py +55 -0
  34. ferp/fscp/protocol/schemas/__init__.py +0 -0
  35. ferp/fscp/protocol/schemas/fscp/1.0/cancel.json +16 -0
  36. ferp/fscp/protocol/schemas/fscp/1.0/definitions.json +29 -0
  37. ferp/fscp/protocol/schemas/fscp/1.0/discriminator.json +14 -0
  38. ferp/fscp/protocol/schemas/fscp/1.0/envelope.json +13 -0
  39. ferp/fscp/protocol/schemas/fscp/1.0/exit.json +20 -0
  40. ferp/fscp/protocol/schemas/fscp/1.0/init.json +36 -0
  41. ferp/fscp/protocol/schemas/fscp/1.0/input_response.json +21 -0
  42. ferp/fscp/protocol/schemas/fscp/1.0/log.json +21 -0
  43. ferp/fscp/protocol/schemas/fscp/1.0/message.json +23 -0
  44. ferp/fscp/protocol/schemas/fscp/1.0/progress.json +23 -0
  45. ferp/fscp/protocol/schemas/fscp/1.0/request_input.json +47 -0
  46. ferp/fscp/protocol/schemas/fscp/1.0/result.json +16 -0
  47. ferp/fscp/protocol/schemas/fscp/__init__.py +0 -0
  48. ferp/fscp/protocol/state.py +16 -0
  49. ferp/fscp/protocol/validator.py +123 -0
  50. ferp/fscp/scripts/__init__.py +0 -0
  51. ferp/fscp/scripts/runtime/__init__.py +4 -0
  52. ferp/fscp/scripts/runtime/__main__.py +40 -0
  53. ferp/fscp/scripts/runtime/errors.py +14 -0
  54. ferp/fscp/scripts/runtime/io.py +64 -0
  55. ferp/fscp/scripts/runtime/script.py +149 -0
  56. ferp/fscp/scripts/runtime/state.py +17 -0
  57. ferp/fscp/scripts/runtime/worker.py +13 -0
  58. ferp/fscp/scripts/sdk.py +548 -0
  59. ferp/fscp/transcript/__init__.py +3 -0
  60. ferp/fscp/transcript/events.py +14 -0
  61. ferp/resources/__init__.py +0 -0
  62. ferp/services/__init__.py +3 -0
  63. ferp/services/file_listing.py +120 -0
  64. ferp/services/monday_sync.py +155 -0
  65. ferp/services/releases.py +214 -0
  66. ferp/services/scripts.py +90 -0
  67. ferp/services/update_check.py +130 -0
  68. ferp/styles/index.tcss +638 -0
  69. ferp/themes/themes.py +238 -0
  70. ferp/widgets/__init__.py +17 -0
  71. ferp/widgets/dialogs.py +167 -0
  72. ferp/widgets/file_tree.py +991 -0
  73. ferp/widgets/forms.py +146 -0
  74. ferp/widgets/output_panel.py +244 -0
  75. ferp/widgets/panels.py +13 -0
  76. ferp/widgets/process_list.py +158 -0
  77. ferp/widgets/readme_modal.py +59 -0
  78. ferp/widgets/scripts.py +192 -0
  79. ferp/widgets/task_capture.py +74 -0
  80. ferp/widgets/task_list.py +493 -0
  81. ferp/widgets/top_bar.py +110 -0
  82. ferp-0.7.1.dist-info/METADATA +128 -0
  83. ferp-0.7.1.dist-info/RECORD +87 -0
  84. ferp-0.7.1.dist-info/WHEEL +5 -0
  85. ferp-0.7.1.dist-info/entry_points.txt +2 -0
  86. ferp-0.7.1.dist-info/licenses/LICENSE +21 -0
  87. ferp-0.7.1.dist-info/top_level.txt +1 -0
ferp/themes/themes.py ADDED
@@ -0,0 +1,238 @@
1
+ from textual.theme import Theme
2
+
3
+ industrial_amber = Theme(
4
+ name="industrial-amber",
5
+ primary="#FFB000",
6
+ secondary="#E09F00",
7
+ accent="#FF6A00",
8
+ foreground="#FFE8B3",
9
+ background="#0E0E0E",
10
+ success="#9FD356",
11
+ warning="#FFB000",
12
+ error="#FF3B30",
13
+ surface="#161616",
14
+ panel="#1F1F1F",
15
+ dark=True,
16
+ variables={
17
+ "footer-key-foreground": "#ffb000",
18
+ "input-selection-background": "#ffb000 25%",
19
+ "block-cursor-text-style": "none",
20
+ },
21
+ )
22
+
23
+ blueprint = Theme(
24
+ name="blueprint",
25
+ primary="#4FC3F7",
26
+ secondary="#0288D1",
27
+ accent="#81D4FA",
28
+ foreground="#E1F5FE",
29
+ background="#0A192F",
30
+ success="#4CAF50",
31
+ warning="#FFC107",
32
+ error="#F44336",
33
+ surface="#102A43",
34
+ panel="#163A5F",
35
+ dark=True,
36
+ variables={
37
+ "footer-key-foreground": "#4fc3f7",
38
+ "input-selection-background": "#0288d1 35%",
39
+ "block-cursor-text-style": "none",
40
+ },
41
+ )
42
+
43
+ paper_ledger = Theme(
44
+ name="paper-ledger",
45
+ primary="#2C3E50",
46
+ secondary="#5D6D7E",
47
+ accent="#1F618D",
48
+ foreground="#1C1C1C",
49
+ background="#FAFAF7",
50
+ success="#2E7D32",
51
+ warning="#F9A825",
52
+ error="#C62828",
53
+ surface="#F0F0EB",
54
+ panel="#E6E6E0",
55
+ dark=False,
56
+ variables={
57
+ "footer-key-foreground": "#2c3e50",
58
+ "input-selection-background": "#1f618d 25%",
59
+ "block-cursor-text-style": "none",
60
+ },
61
+ )
62
+
63
+ neo_matrix = Theme(
64
+ name="neo-matrix",
65
+ primary="#37FF14DC",
66
+ secondary="#0F7B0F",
67
+ accent="#ABFCAB",
68
+ foreground="#B6FFB6",
69
+ background="#000B00",
70
+ success="#00FF66",
71
+ warning="#FFB000",
72
+ error="#FF3B30",
73
+ surface="#000B00",
74
+ panel="#000B00",
75
+ dark=True,
76
+ variables={
77
+ "footer-key-foreground": "#39ff14",
78
+ "input-selection-background": "#39ff14 25%",
79
+ "block-cursor-text-style": "none",
80
+ },
81
+ )
82
+
83
+ slate_copper = Theme(
84
+ name="slate-copper",
85
+ primary="#C97C5D",
86
+ secondary="#8C5A3C",
87
+ # accent="#E3A587",
88
+ accent="#DFB6A2",
89
+ foreground="#EDE7E3",
90
+ background="#2B2B2B",
91
+ success="#8BC34A",
92
+ warning="#FFB74D",
93
+ error="#E57373",
94
+ surface="#353535",
95
+ panel="#404040",
96
+ dark=True,
97
+ variables={
98
+ "footer-key-foreground": "#c97c5d",
99
+ "input-selection-background": "#c97c5d 30%",
100
+ "block-cursor-text-style": "none",
101
+ },
102
+ )
103
+
104
+ burgundy = Theme(
105
+ name="ron-burgundy",
106
+ primary="#7A1E2B", # deep burgundy
107
+ secondary="#A63A4A", # lighter wine
108
+ accent="#C94C5C", # rose accent
109
+ foreground="#E6DCDC", # warm off-white
110
+ background="#121012", # near-black, warm
111
+ success="#4FAF8F", # muted teal-green (contrasts burgundy well)
112
+ warning="#D4A017", # antique gold
113
+ error="#C83A3A", # restrained red
114
+ surface="#1C171A", # panels / cards
115
+ panel="#241C20", # raised panels
116
+ dark=True,
117
+ variables={
118
+ "footer-key-foreground": "#C94C5C",
119
+ "input-selection-background": "#7A1E2B 40%",
120
+ "block-cursor-text-style": "none",
121
+ },
122
+ )
123
+
124
+ zapp_brannigan = Theme(
125
+ name="zapp-brannigan",
126
+ primary="#b69003",
127
+ secondary="#1F6F78",
128
+ warning="#E5533D",
129
+ error="#B11226",
130
+ success="#4CAF73",
131
+ accent="#800e13",
132
+ foreground="#ECE8E1",
133
+ background="#0E1418",
134
+ surface="#172026",
135
+ panel="#1E2A32",
136
+ dark=True,
137
+ variables={
138
+ "block-cursor-text-style": "none",
139
+ "footer-key-foreground": "#C9A23F",
140
+ },
141
+ )
142
+
143
+
144
+ black_and_white = Theme(
145
+ name="black_and_white",
146
+ primary="#FFFFFF",
147
+ secondary="#C7C5C5",
148
+ warning="#E5533D",
149
+ error="#B11226",
150
+ success="#4CAF73",
151
+ accent="#AAAAAA",
152
+ foreground="#ECE8E1",
153
+ background="#000000",
154
+ surface="#292929",
155
+ panel="#000000",
156
+ dark=True,
157
+ variables={
158
+ "block-cursor-text-style": "none",
159
+ "footer-key-foreground": "#FFFFFF",
160
+ },
161
+ )
162
+
163
+
164
+ nashua_kyle = Theme(
165
+ name="nashua-kyle",
166
+ primary="#A3B18A",
167
+ secondary="#606c38",
168
+ warning="#ca6c00",
169
+ error="#c1121f",
170
+ success="#405824",
171
+ accent="#bc6c25",
172
+ foreground="#1e2211",
173
+ background="#EDE0D4",
174
+ surface="#a28c73",
175
+ panel="#c6ac8f",
176
+ dark=False,
177
+ variables={
178
+ "block-cursor-text-style": "none",
179
+ "footer-key-foreground": "#606c38",
180
+ "footer-description-foreground": "#EDE0D4",
181
+ },
182
+ )
183
+
184
+
185
+ hey_noodle = Theme(
186
+ name="hey-noodle",
187
+ primary="#748cab",
188
+ secondary="#1d2d44",
189
+ warning="#ca6c00",
190
+ error="#c1121f",
191
+ success="#679436",
192
+ accent="#959b9d",
193
+ foreground="#1d2d44",
194
+ background="#dbe9ee",
195
+ surface="#accbe1",
196
+ panel="#edede9",
197
+ dark=False,
198
+ variables={
199
+ "block-cursor-text-style": "none",
200
+ "footer-key-foreground": "#1d2d44",
201
+ "footer-description-foreground": "#1d2d44",
202
+ },
203
+ )
204
+
205
+
206
+ dusty_dawn = Theme(
207
+ name="dusty-dawn",
208
+ primary="#49111C",
209
+ secondary="#2C0B11",
210
+ warning="#b25f00",
211
+ error="#a8000b",
212
+ success="#53772C",
213
+ accent="#5E503F",
214
+ foreground="#F3E9EB",
215
+ background="#A9927D",
216
+ surface="#998163",
217
+ panel="#816D55",
218
+ dark=False,
219
+ variables={
220
+ "block-cursor-text-style": "none",
221
+ "footer-key-foreground": "#49111C",
222
+ },
223
+ )
224
+
225
+
226
+ ALL_THEMES: list[Theme] = [
227
+ industrial_amber,
228
+ blueprint,
229
+ paper_ledger,
230
+ neo_matrix,
231
+ slate_copper,
232
+ burgundy,
233
+ black_and_white,
234
+ nashua_kyle,
235
+ zapp_brannigan,
236
+ hey_noodle,
237
+ dusty_dawn,
238
+ ]
@@ -0,0 +1,17 @@
1
+ from .dialogs import ConfirmDialog
2
+ from .file_tree import FileItem, FileListingEntry, FileTree, FileTreeFilterWidget
3
+ from .output_panel import ScriptOutputPanel
4
+ from .panels import ContentPanel
5
+ from .scripts import ScriptItem, ScriptManager
6
+
7
+ __all__ = [
8
+ "FileTree",
9
+ "FileListingEntry",
10
+ "FileItem",
11
+ "FileTreeFilterWidget",
12
+ "ScriptManager",
13
+ "ScriptItem",
14
+ "ContentPanel",
15
+ "ScriptOutputPanel",
16
+ "ConfirmDialog",
17
+ ]
@@ -0,0 +1,167 @@
1
+ from rich.style import Style
2
+ from rich.text import Text
3
+ from textual.containers import Container, Horizontal, Vertical
4
+ from textual.screen import ModalScreen
5
+ from textual.widgets import Button, DataTable, Input, Label, Select
6
+
7
+
8
+ class ConfirmDialog(ModalScreen[bool | None]):
9
+ def __init__(self, message: str, id="confirm_dialog"):
10
+ super().__init__(id=id)
11
+ self.message = message
12
+
13
+ def compose(self):
14
+ yield Container(
15
+ Label(self.message, id="dialog_message"),
16
+ Horizontal(
17
+ Button("Yes", id="yes", variant="primary", flat=True),
18
+ Button("No", id="cancel", flat=True),
19
+ classes="dialog_buttons",
20
+ ),
21
+ id="dialog_container",
22
+ )
23
+
24
+ def on_button_pressed(self, event):
25
+ if event.button.id == "cancel":
26
+ self.dismiss(False)
27
+ return
28
+ self.dismiss(event.button.id == "yes")
29
+
30
+ def on_screen_resume(self) -> None:
31
+ if getattr(self, "_dismiss_on_resume", False):
32
+ self.dismiss(None)
33
+
34
+
35
+ class BulkRenameConfirmDialog(ModalScreen[bool | None]):
36
+ def __init__(
37
+ self,
38
+ title: str,
39
+ rows: list[tuple[str, str]],
40
+ more_count: int = 0,
41
+ ) -> None:
42
+ super().__init__(id="bulk_rename_confirm_dialog")
43
+ self.dialog_title: str = title or ""
44
+ self.rows: list[tuple[str, str]] = rows
45
+ self.more_count = more_count
46
+
47
+ def compose(self):
48
+ table = DataTable(
49
+ id="bulk_rename_preview_table", show_cursor=False, disabled=True
50
+ )
51
+ table.add_column("From", width=75)
52
+ table.add_column("To", width=75)
53
+ if self.rows:
54
+ success_color = self.app.theme_variables["success"]
55
+ error_color = self.app.theme_variables["error"]
56
+ styled_rows = [
57
+ (
58
+ Text(src, style=Style(color=error_color)),
59
+ Text(dest, style=Style(color=success_color)),
60
+ )
61
+ for src, dest in self.rows
62
+ ]
63
+ table.add_rows(styled_rows)
64
+ yield Container(
65
+ Label(self.dialog_title, id="bulk_rename_dialog_title"),
66
+ table,
67
+ Label(
68
+ f"... and {self.more_count} more.",
69
+ id="bulk_rename_dialog_message",
70
+ classes="" if self.more_count else "hidden",
71
+ ),
72
+ Horizontal(
73
+ Button("Yes", id="bulk_rename_yes", variant="primary", flat=True),
74
+ Button("No", id="bulk_rename_cancel", flat=True),
75
+ classes="dialog_buttons",
76
+ ),
77
+ id="bulk_rename_dialog_container",
78
+ )
79
+
80
+ def on_button_pressed(self, event):
81
+ if event.button.id == "bulk_rename_cancel":
82
+ self.dismiss(False)
83
+ return
84
+ self.dismiss(event.button.id == "bulk_rename_yes")
85
+
86
+ def on_screen_resume(self) -> None:
87
+ if getattr(self, "_dismiss_on_resume", False):
88
+ self.dismiss(None)
89
+
90
+
91
+ class InputDialog(ModalScreen[str | None]):
92
+ def __init__(
93
+ self, message: str, id: str = "input_dialog", default: str | None = None
94
+ ) -> None:
95
+ super().__init__(id=id)
96
+ self._message = message
97
+ self._default = default
98
+
99
+ def compose(self):
100
+ yield Vertical(
101
+ Label(self._message, id="dialog_message"),
102
+ Container(
103
+ Input(value=self._default or "", id="input"),
104
+ id="input_container",
105
+ ),
106
+ Horizontal(
107
+ Button("OK", id="ok", variant="primary", flat=True),
108
+ Button("Cancel", id="cancel", flat=True),
109
+ classes="dialog_buttons",
110
+ ),
111
+ id="dialog_container",
112
+ )
113
+
114
+ def on_mount(self) -> None:
115
+ self.query_one(Input).focus()
116
+
117
+ def on_button_pressed(self, event: Button.Pressed) -> None:
118
+ if event.button.id == "ok":
119
+ value = self.query_one(Input).value
120
+ self.dismiss(value)
121
+ else:
122
+ self.dismiss(None)
123
+
124
+
125
+ class SelectDialog(ModalScreen[str | None]):
126
+ def __init__(
127
+ self,
128
+ message: str,
129
+ options: list[str],
130
+ *,
131
+ id: str = "select_dialog",
132
+ ) -> None:
133
+ super().__init__(id=id)
134
+ self._message = message
135
+ self._options = options
136
+
137
+ def compose(self):
138
+ select_options = [(option, option) for option in self._options]
139
+ default = self._options[0] if self._options else ""
140
+ yield Vertical(
141
+ Label(self._message, id="dialog_message"),
142
+ Container(
143
+ Select(
144
+ select_options,
145
+ value=default,
146
+ allow_blank=False,
147
+ id="select",
148
+ ),
149
+ id="select_container",
150
+ ),
151
+ Horizontal(
152
+ Button("OK", id="ok", variant="primary", flat=True),
153
+ Button("Cancel", id="cancel", flat=True),
154
+ classes="dialog_buttons",
155
+ ),
156
+ id="dialog_container",
157
+ )
158
+
159
+ def on_mount(self) -> None:
160
+ self.query_one(Select).focus()
161
+
162
+ def on_button_pressed(self, event: Button.Pressed) -> None:
163
+ if event.button.id == "ok":
164
+ value = self.query_one(Select).value
165
+ self.dismiss(str(value) if value is not None else None)
166
+ else:
167
+ self.dismiss(None)