csdl-explore 0.1.0__tar.gz

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 (67) hide show
  1. csdl_explore-0.1.0/.claude/skills/textual/SKILL.md +240 -0
  2. csdl_explore-0.1.0/.claude/skills/textual/references/css.md +255 -0
  3. csdl_explore-0.1.0/.claude/skills/textual/references/examples.md +359 -0
  4. csdl_explore-0.1.0/.claude/skills/textual/references/widgets.md +132 -0
  5. csdl_explore-0.1.0/.gitignore +207 -0
  6. csdl_explore-0.1.0/.superpowers/brainstorm/1839-1774958493/content/color-direction.html +61 -0
  7. csdl_explore-0.1.0/.superpowers/brainstorm/1839-1774958493/content/waiting.html +3 -0
  8. csdl_explore-0.1.0/.superpowers/brainstorm/1839-1774958493/content/welcome-mockup.html +71 -0
  9. csdl_explore-0.1.0/.superpowers/brainstorm/1839-1774958493/state/server-stopped +1 -0
  10. csdl_explore-0.1.0/.superpowers/brainstorm/1839-1774958493/state/server.pid +1 -0
  11. csdl_explore-0.1.0/CLAUDE.md +102 -0
  12. csdl_explore-0.1.0/LICENSE +21 -0
  13. csdl_explore-0.1.0/PKG-INFO +350 -0
  14. csdl_explore-0.1.0/README.md +322 -0
  15. csdl_explore-0.1.0/docs/plans/2026-02-08-navigation-graph-design.md +278 -0
  16. csdl_explore-0.1.0/docs/plans/2026-03-05-query-cli-command-design.md +146 -0
  17. csdl_explore-0.1.0/docs/plans/2026-03-05-query-command-implementation.md +344 -0
  18. csdl_explore-0.1.0/docs/plans/2026-03-10-cli-improvements.md +907 -0
  19. csdl_explore-0.1.0/docs/superpowers/plans/2026-03-31-visual-refresh.md +754 -0
  20. csdl_explore-0.1.0/docs/superpowers/specs/2026-03-31-visual-refresh-design.md +103 -0
  21. csdl_explore-0.1.0/pyproject.toml +44 -0
  22. csdl_explore-0.1.0/samples/picklist.json +666 -0
  23. csdl_explore-0.1.0/samples/test001.edmx.xml +1150 -0
  24. csdl_explore-0.1.0/samples/test002.edmx.xml +268 -0
  25. csdl_explore-0.1.0/samples/test003.edmx.xml +912 -0
  26. csdl_explore-0.1.0/samples/test004.edmx.xml +1541 -0
  27. csdl_explore-0.1.0/src/csdl_explore/__init__.py +7 -0
  28. csdl_explore-0.1.0/src/csdl_explore/app.py +500 -0
  29. csdl_explore-0.1.0/src/csdl_explore/cli.py +812 -0
  30. csdl_explore-0.1.0/src/csdl_explore/exit_codes.py +7 -0
  31. csdl_explore-0.1.0/src/csdl_explore/explorer.py +330 -0
  32. csdl_explore-0.1.0/src/csdl_explore/formatters.py +641 -0
  33. csdl_explore-0.1.0/src/csdl_explore/parser.py +288 -0
  34. csdl_explore-0.1.0/src/csdl_explore/repl.py +627 -0
  35. csdl_explore-0.1.0/src/csdl_explore/sap_client.py +382 -0
  36. csdl_explore-0.1.0/src/csdl_explore/themes.py +83 -0
  37. csdl_explore-0.1.0/src/csdl_explore/widgets/__init__.py +37 -0
  38. csdl_explore-0.1.0/src/csdl_explore/widgets/auth_modal.py +169 -0
  39. csdl_explore-0.1.0/src/csdl_explore/widgets/connection_panel.py +189 -0
  40. csdl_explore-0.1.0/src/csdl_explore/widgets/details_tab.py +109 -0
  41. csdl_explore-0.1.0/src/csdl_explore/widgets/entity_pane.py +61 -0
  42. csdl_explore-0.1.0/src/csdl_explore/widgets/entity_pane.tcss +2 -0
  43. csdl_explore-0.1.0/src/csdl_explore/widgets/entity_tree.py +138 -0
  44. csdl_explore-0.1.0/src/csdl_explore/widgets/filter_bar.py +24 -0
  45. csdl_explore-0.1.0/src/csdl_explore/widgets/filterable_table.py +87 -0
  46. csdl_explore-0.1.0/src/csdl_explore/widgets/global_search.py +193 -0
  47. csdl_explore-0.1.0/src/csdl_explore/widgets/graph_tab.py +70 -0
  48. csdl_explore-0.1.0/src/csdl_explore/widgets/json_viewer_modal.py +112 -0
  49. csdl_explore-0.1.0/src/csdl_explore/widgets/nav_graph.py +443 -0
  50. csdl_explore-0.1.0/src/csdl_explore/widgets/picklist_entities_tab.py +82 -0
  51. csdl_explore-0.1.0/src/csdl_explore/widgets/picklist_impact_tab.py +78 -0
  52. csdl_explore-0.1.0/src/csdl_explore/widgets/picklist_overview_tab.py +66 -0
  53. csdl_explore-0.1.0/src/csdl_explore/widgets/picklist_pane.py +68 -0
  54. csdl_explore-0.1.0/src/csdl_explore/widgets/picklist_values_tab.py +211 -0
  55. csdl_explore-0.1.0/src/csdl_explore/widgets/properties_tab.py +107 -0
  56. csdl_explore-0.1.0/src/csdl_explore/widgets/query_builder.py +430 -0
  57. csdl_explore-0.1.0/src/csdl_explore/widgets/query_builder.tcss +119 -0
  58. csdl_explore-0.1.0/src/csdl_explore/widgets/query_tab.py +120 -0
  59. csdl_explore-0.1.0/src/csdl_explore/widgets/record_view_modal.py +131 -0
  60. csdl_explore-0.1.0/src/csdl_explore/widgets/results_viewer.py +282 -0
  61. csdl_explore-0.1.0/src/csdl_explore/widgets/search_results.py +42 -0
  62. csdl_explore-0.1.0/src/csdl_explore/widgets/welcome_tab.py +96 -0
  63. csdl_explore-0.1.0/test_app.py +40 -0
  64. csdl_explore-0.1.0/tests/test_exit_codes.py +12 -0
  65. csdl_explore-0.1.0/tests/test_explorer.py +28 -0
  66. csdl_explore-0.1.0/tests/test_formatters.py +193 -0
  67. csdl_explore-0.1.0/tests/test_sap_errors.py +30 -0
@@ -0,0 +1,240 @@
1
+ ---
2
+ name: textual
3
+ description: Build terminal user interface (TUI) applications with the Textual framework. Use when creating new Textual apps, adding screens/widgets, styling with TCSS, handling events and reactivity, testing TUI apps, or any task involving "textual", "TUI", or terminal-based Python applications.
4
+ ---
5
+
6
+ # Textual TUI Framework
7
+
8
+ Build terminal applications with Textual's web-inspired architecture: App → Screen → Widget.
9
+
10
+ ## Quick Start
11
+
12
+ ```python
13
+ from textual.app import App, ComposeResult
14
+ from textual.widgets import Header, Footer, Static
15
+
16
+ class MyApp(App):
17
+ CSS_PATH = "styles.tcss"
18
+ BINDINGS = [("q", "quit", "Quit"), ("d", "toggle_dark", "Dark Mode")]
19
+
20
+ def compose(self) -> ComposeResult:
21
+ yield Header()
22
+ yield Static("Hello, World!")
23
+ yield Footer()
24
+
25
+ def action_toggle_dark(self) -> None:
26
+ self.theme = "textual-dark" if self.theme == "textual-light" else "textual-light"
27
+
28
+ if __name__ == "__main__":
29
+ MyApp().run()
30
+ ```
31
+
32
+ ## Core Concepts
33
+
34
+ ### Widget Lifecycle
35
+ 1. `__init__()` → `compose()` → `on_mount()` → `on_show()`/`on_hide()` → `on_unmount()`
36
+
37
+ ### Reactivity
38
+ ```python
39
+ from textual.reactive import reactive, var
40
+
41
+ class MyWidget(Widget):
42
+ count = reactive(0) # Triggers refresh on change
43
+ internal = var("") # No automatic refresh
44
+
45
+ def watch_count(self, new_value: int) -> None:
46
+ """Called when count changes."""
47
+ self.styles.background = "green" if new_value > 0 else "red"
48
+
49
+ def validate_count(self, value: int) -> int:
50
+ """Constrain values."""
51
+ return max(0, min(100, value))
52
+ ```
53
+
54
+ ### Events and Messages
55
+ ```python
56
+ from textual import on
57
+ from textual.message import Message
58
+
59
+ class MyWidget(Widget):
60
+ class Selected(Message):
61
+ def __init__(self, value: str) -> None:
62
+ self.value = value
63
+ super().__init__()
64
+
65
+ def on_click(self) -> None:
66
+ self.post_message(self.Selected("item"))
67
+
68
+ class MyApp(App):
69
+ # Handler naming: on_<widget>_<message>
70
+ def on_button_pressed(self, event: Button.Pressed) -> None:
71
+ self.log(f"Button {event.button.id} pressed")
72
+
73
+ @on(Button.Pressed, "#submit") # CSS selector filtering
74
+ def handle_submit(self) -> None:
75
+ pass
76
+ ```
77
+
78
+ ### Data Flow
79
+ - **Attributes down**: Parent sets child properties directly
80
+ - **Messages up**: Child posts messages to parent via `post_message()`
81
+
82
+ ## Screens
83
+
84
+ ```python
85
+ from textual.screen import Screen
86
+
87
+ class WelcomeScreen(Screen):
88
+ BINDINGS = [("escape", "app.pop_screen", "Back")]
89
+
90
+ def compose(self) -> ComposeResult:
91
+ yield Static("Welcome!")
92
+ yield Button("Continue", id="continue")
93
+
94
+ def on_button_pressed(self, event: Button.Pressed) -> None:
95
+ if event.button.id == "continue":
96
+ self.app.push_screen("main")
97
+
98
+ class MyApp(App):
99
+ SCREENS = {"welcome": WelcomeScreen, "main": MainScreen}
100
+
101
+ def on_mount(self) -> None:
102
+ self.push_screen("welcome")
103
+ ```
104
+
105
+ ## Custom Widgets
106
+
107
+ ### Simple Widget
108
+ ```python
109
+ class Greeting(Widget):
110
+ def render(self) -> RenderResult:
111
+ return "Hello, [bold]World[/bold]!"
112
+ ```
113
+
114
+ ### Compound Widget
115
+ ```python
116
+ class LabeledButton(Widget):
117
+ DEFAULT_CSS = """
118
+ LabeledButton { layout: horizontal; height: auto; }
119
+ LabeledButton Label { width: 1fr; }
120
+ """
121
+
122
+ def __init__(self, label: str, button_text: str) -> None:
123
+ self.label_text = label
124
+ self.button_text = button_text
125
+ super().__init__()
126
+
127
+ def compose(self) -> ComposeResult:
128
+ yield Label(self.label_text)
129
+ yield Button(self.button_text)
130
+ ```
131
+
132
+ ### Focusable Widget
133
+ ```python
134
+ class Counter(Widget):
135
+ can_focus = True
136
+ BINDINGS = [("up", "increment", "+"), ("down", "decrement", "-")]
137
+ count = reactive(0)
138
+
139
+ def action_increment(self) -> None:
140
+ self.count += 1
141
+ ```
142
+
143
+ ## Layout Patterns
144
+
145
+ ### Containers
146
+ ```python
147
+ from textual.containers import Horizontal, Vertical, Grid, VerticalScroll
148
+
149
+ def compose(self) -> ComposeResult:
150
+ with Vertical():
151
+ with Horizontal():
152
+ yield Button("Left")
153
+ yield Button("Right")
154
+ with VerticalScroll():
155
+ for i in range(100):
156
+ yield Label(f"Item {i}")
157
+ ```
158
+
159
+ ### Grid CSS
160
+ ```css
161
+ Grid {
162
+ layout: grid;
163
+ grid-size: 3 2; /* columns rows */
164
+ grid-columns: 1fr 2fr 1fr;
165
+ grid-gutter: 1 2;
166
+ }
167
+ #wide { column-span: 2; }
168
+ ```
169
+
170
+ ### Docking
171
+ ```css
172
+ #header { dock: top; height: 3; }
173
+ #sidebar { dock: left; width: 25; }
174
+ #footer { dock: bottom; height: 1; }
175
+ ```
176
+
177
+ ## Workers (Async)
178
+
179
+ ```python
180
+ from textual import work
181
+
182
+ class MyApp(App):
183
+ @work(exclusive=True) # Cancels previous
184
+ async def fetch_data(self, url: str) -> None:
185
+ async with httpx.AsyncClient() as client:
186
+ response = await client.get(url)
187
+ self.query_one("#result").update(response.text)
188
+
189
+ @work(thread=True) # For sync APIs
190
+ def sync_operation(self) -> None:
191
+ result = blocking_call()
192
+ self.call_from_thread(self.update_ui, result)
193
+ ```
194
+
195
+ ## Testing
196
+
197
+ ```python
198
+ async def test_app():
199
+ app = MyApp()
200
+ async with app.run_test() as pilot:
201
+ await pilot.press("enter")
202
+ await pilot.click("#button")
203
+ await pilot.pause() # Wait for messages
204
+ assert app.query_one("#status").render() == "Done"
205
+ ```
206
+
207
+ ## Common Operations
208
+
209
+ ```python
210
+ # Query widgets
211
+ self.query_one("#id")
212
+ self.query_one(Button)
213
+ self.query(".class")
214
+
215
+ # CSS classes
216
+ widget.add_class("active")
217
+ widget.toggle_class("visible")
218
+ widget.set_class(condition, "active")
219
+
220
+ # Visibility
221
+ widget.display = True/False
222
+
223
+ # Mount/remove
224
+ self.mount(NewWidget())
225
+ widget.remove()
226
+
227
+ # Timers
228
+ self.set_interval(1.0, callback)
229
+ self.set_timer(5.0, callback)
230
+
231
+ # Exit
232
+ self.exit(return_code=0)
233
+ ```
234
+
235
+ ## References
236
+
237
+ - **Widget catalog and messages**: See [references/widgets.md](references/widgets.md)
238
+ - **CSS properties and selectors**: See [references/css.md](references/css.md)
239
+ - **Complete examples**: See [references/examples.md](references/examples.md)
240
+ - **Official docs**: https://textual.textualize.io/
@@ -0,0 +1,255 @@
1
+ # Textual CSS (TCSS) Reference
2
+
3
+ ## Table of Contents
4
+ - [Selectors](#selectors)
5
+ - [Layout Properties](#layout-properties)
6
+ - [Sizing Properties](#sizing-properties)
7
+ - [Positioning Properties](#positioning-properties)
8
+ - [Appearance Properties](#appearance-properties)
9
+ - [Text Properties](#text-properties)
10
+ - [Scrolling Properties](#scrolling-properties)
11
+ - [Animation Properties](#animation-properties)
12
+ - [Theme Variables](#theme-variables)
13
+ - [Pseudo-classes](#pseudo-classes)
14
+
15
+ ## Selectors
16
+
17
+ ```css
18
+ /* Type selector - matches Python class name */
19
+ Button { }
20
+
21
+ /* ID selector */
22
+ #sidebar { }
23
+
24
+ /* Class selector */
25
+ .error { }
26
+
27
+ /* Compound selectors */
28
+ Button.primary { }
29
+
30
+ /* Descendant combinator */
31
+ #dialog Button { }
32
+
33
+ /* Child combinator */
34
+ Container > Button { }
35
+
36
+ /* Multiple selectors */
37
+ #submit, #cancel { }
38
+ ```
39
+
40
+ ## Nested CSS
41
+
42
+ ```css
43
+ #container {
44
+ background: $surface;
45
+
46
+ .item {
47
+ padding: 1;
48
+
49
+ &:hover {
50
+ background: $primary;
51
+ }
52
+
53
+ &.-active {
54
+ border: solid green;
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ ## Layout Properties
61
+
62
+ ```css
63
+ layout: vertical; /* vertical, horizontal, grid */
64
+
65
+ /* Grid layout */
66
+ grid-size: 3 2; /* columns rows */
67
+ grid-columns: 1fr 2fr 1fr;
68
+ grid-rows: auto 1fr;
69
+ grid-gutter: 1 2; /* vertical horizontal */
70
+ column-span: 2;
71
+ row-span: 2;
72
+ ```
73
+
74
+ ## Sizing Properties
75
+
76
+ ```css
77
+ width: 50%; /* auto, %, fr, cells */
78
+ height: 100%;
79
+ min-width: 20;
80
+ max-width: 80;
81
+ min-height: 10;
82
+ max-height: 50;
83
+
84
+ margin: 1 2; /* vertical horizontal */
85
+ margin: 1 2 1 2; /* top right bottom left */
86
+ padding: 1 2;
87
+ padding: 1 2 1 2;
88
+ ```
89
+
90
+ ## Positioning Properties
91
+
92
+ ```css
93
+ dock: top; /* top, right, bottom, left */
94
+ offset: 5 10; /* x y offset */
95
+ offset-x: -100%;
96
+ offset-y: 50%;
97
+ layer: overlay; /* layer name */
98
+ ```
99
+
100
+ ### Layers
101
+ ```css
102
+ Screen {
103
+ layers: base overlay modal;
104
+ }
105
+
106
+ #background { layer: base; }
107
+ #popup { layer: overlay; }
108
+ #dialog { layer: modal; }
109
+ ```
110
+
111
+ ## Appearance Properties
112
+
113
+ ```css
114
+ background: darkblue;
115
+ color: white;
116
+
117
+ /* Borders */
118
+ border: solid green; /* none, solid, double, round, heavy, tall, wide */
119
+ border-top: double red;
120
+ border-right: solid blue;
121
+ border-bottom: heavy green;
122
+ border-left: round yellow;
123
+
124
+ outline: dashed red;
125
+ opacity: 0.5;
126
+ ```
127
+
128
+ ## Text Properties
129
+
130
+ ```css
131
+ text-align: center; /* left, center, right */
132
+ content-align: center middle; /* horizontal vertical */
133
+ text-style: bold italic; /* bold, italic, underline, reverse, strike */
134
+ ```
135
+
136
+ ## Scrolling Properties
137
+
138
+ ```css
139
+ overflow: auto; /* auto, hidden, scroll */
140
+ overflow-x: auto;
141
+ overflow-y: scroll;
142
+ scrollbar-gutter: stable;
143
+ ```
144
+
145
+ ## Animation Properties
146
+
147
+ ```css
148
+ transition: background 500ms;
149
+ transition: offset 200ms ease-in-out;
150
+ transition: opacity 300ms linear;
151
+
152
+ /* Multiple transitions */
153
+ transition: background 200ms, offset 300ms;
154
+ ```
155
+
156
+ ### Easing Functions
157
+ - `linear`
158
+ - `ease-in`
159
+ - `ease-out`
160
+ - `ease-in-out`
161
+
162
+ ## Theme Variables
163
+
164
+ ### Core Colors
165
+ ```css
166
+ $primary
167
+ $secondary
168
+ $success
169
+ $warning
170
+ $error
171
+ $text
172
+ $background
173
+ $surface
174
+ $panel
175
+ ```
176
+
177
+ ### Color Variants
178
+ ```css
179
+ $primary-lighten-1
180
+ $primary-lighten-2
181
+ $primary-lighten-3
182
+ $primary-darken-1
183
+ $primary-darken-2
184
+ $primary-darken-3
185
+ ```
186
+
187
+ ### Custom Variables
188
+ ```css
189
+ $my-color: dodgerblue;
190
+ $spacing: 2;
191
+
192
+ .widget {
193
+ background: $my-color;
194
+ padding: $spacing;
195
+ }
196
+ ```
197
+
198
+ ## Pseudo-classes
199
+
200
+ ```css
201
+ Button:hover { background: lightblue; }
202
+ Button:focus { border: double green; }
203
+ Button:disabled { opacity: 0.5; }
204
+
205
+ /* Custom state classes (prefixed with -) */
206
+ .sidebar.-visible { offset-x: 0; }
207
+ .item.-selected { background: $primary; }
208
+ ```
209
+
210
+ ## Common Patterns
211
+
212
+ ### Animated Sidebar
213
+ ```css
214
+ Sidebar {
215
+ width: 30;
216
+ dock: left;
217
+ offset-x: -100%;
218
+ transition: offset 200ms;
219
+
220
+ &.-visible {
221
+ offset-x: 0;
222
+ }
223
+ }
224
+ ```
225
+
226
+ ### Centered Modal
227
+ ```css
228
+ #modal {
229
+ layer: modal;
230
+ width: 60;
231
+ height: 20;
232
+ background: $surface;
233
+ border: thick $primary;
234
+ }
235
+
236
+ Screen {
237
+ align: center middle;
238
+ layers: base modal;
239
+ }
240
+ ```
241
+
242
+ ### Responsive Grid
243
+ ```css
244
+ Grid {
245
+ layout: grid;
246
+ grid-size: 3;
247
+ grid-gutter: 1;
248
+ }
249
+
250
+ @media (max-width: 60) {
251
+ Grid {
252
+ grid-size: 2;
253
+ }
254
+ }
255
+ ```