KTrex 0.1.5__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.
- ktrex-0.1.5/KTrex.egg-info/PKG-INFO +208 -0
- ktrex-0.1.5/KTrex.egg-info/SOURCES.txt +16 -0
- ktrex-0.1.5/KTrex.egg-info/dependency_links.txt +1 -0
- ktrex-0.1.5/KTrex.egg-info/entry_points.txt +2 -0
- ktrex-0.1.5/KTrex.egg-info/top_level.txt +1 -0
- ktrex-0.1.5/LICENSE +21 -0
- ktrex-0.1.5/PKG-INFO +208 -0
- ktrex-0.1.5/README.md +197 -0
- ktrex-0.1.5/ktrex/__init__.py +0 -0
- ktrex-0.1.5/ktrex/fs.py +49 -0
- ktrex-0.1.5/ktrex/main.py +46 -0
- ktrex-0.1.5/ktrex/presets.py +27 -0
- ktrex-0.1.5/ktrex/render.py +44 -0
- ktrex-0.1.5/ktrex/state.py +11 -0
- ktrex-0.1.5/ktrex/tree.py +26 -0
- ktrex-0.1.5/ktrex/tui.py +251 -0
- ktrex-0.1.5/pyproject.toml +21 -0
- ktrex-0.1.5/setup.cfg +4 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: KTrex
|
|
3
|
+
Version: 0.1.5
|
|
4
|
+
Summary: Interactive terminal-based directory structure builder
|
|
5
|
+
Author: AK
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# KTrex
|
|
13
|
+
|
|
14
|
+
**KTrex** is an interactive, terminal-based directory and file structure builder.
|
|
15
|
+
It lets you visually create, navigate, undo, save, load, and reuse project layouts
|
|
16
|
+
directly from a curses-based TUI — safely and deterministically.
|
|
17
|
+
|
|
18
|
+
> Think: `mkdir` + `tree` + `undo` + `presets` + `resume`, all in one tool.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- Interactive terminal UI (arrow-key driven)
|
|
25
|
+
- Create directories and files visually
|
|
26
|
+
- Safe undo (never deletes non-empty directories)
|
|
27
|
+
- Presets (builtin or external JSON)
|
|
28
|
+
- Save project structure to JSON
|
|
29
|
+
- Load and resume saved structures
|
|
30
|
+
- Non-destructive by design (no overwrites, no forced deletes)
|
|
31
|
+
- Works offline, zero runtime dependencies
|
|
32
|
+
- Scrollable tree view (keyboard + mouse)
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
### Using pip (recommended)
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install KTrex
|
|
42
|
+
````
|
|
43
|
+
|
|
44
|
+
### Editable install (for development)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
git clone https://github.com/yourusername/KTrex.git
|
|
48
|
+
cd KTrex
|
|
49
|
+
pip install -e .
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## ▶️ Usage
|
|
55
|
+
|
|
56
|
+
Run KTrex inside any directory:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
KTrex
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
You’ll be dropped into an interactive TUI showing your project tree and available actions.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Controls
|
|
67
|
+
|
|
68
|
+
| Key | Action |
|
|
69
|
+
|-----------------------|--------------------------------|
|
|
70
|
+
| ↑ / ↓ | Navigate menu |
|
|
71
|
+
| Enter | Select highlighted action |
|
|
72
|
+
| PageUp / PageDown | Scroll tree view |
|
|
73
|
+
| Ctrl + U / Ctrl + D | Scroll tree view |
|
|
74
|
+
| Mouse Wheel | Scroll tree view |
|
|
75
|
+
| Any key | Dismiss messages / dialogs |
|
|
76
|
+
| Quit | Exit KTrex safely |
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Actions Explained
|
|
81
|
+
|
|
82
|
+
### Create Directory
|
|
83
|
+
|
|
84
|
+
* Creates a new directory in the current location
|
|
85
|
+
* Automatically enters the directory
|
|
86
|
+
|
|
87
|
+
### Create File
|
|
88
|
+
|
|
89
|
+
* Creates a file in the current directory
|
|
90
|
+
* Does **not** change navigation
|
|
91
|
+
|
|
92
|
+
### Undo
|
|
93
|
+
|
|
94
|
+
* Reverts the most recent create action
|
|
95
|
+
* Only removes files or **empty directories**
|
|
96
|
+
* Never deletes non-empty directories
|
|
97
|
+
|
|
98
|
+
### Load Preset
|
|
99
|
+
|
|
100
|
+
* Load a predefined structure by name or file path
|
|
101
|
+
* Presets are **additive**, never destructive
|
|
102
|
+
|
|
103
|
+
### Save Structure
|
|
104
|
+
|
|
105
|
+
* Serialize the entire project tree to a JSON file
|
|
106
|
+
* Useful for reuse, backups, or AI-assisted generation
|
|
107
|
+
|
|
108
|
+
### Load Structure
|
|
109
|
+
|
|
110
|
+
* Load a previously saved structure JSON
|
|
111
|
+
* Applies safely without removing existing files
|
|
112
|
+
|
|
113
|
+
### Go Back
|
|
114
|
+
|
|
115
|
+
* Navigate to parent directory
|
|
116
|
+
* Cannot escape project root
|
|
117
|
+
|
|
118
|
+
### Help
|
|
119
|
+
|
|
120
|
+
* View built-in documentation and keybindings
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Presets
|
|
125
|
+
|
|
126
|
+
### Built-in presets
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
ml_project
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Usage:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
Load Preset → ml_project
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### External presets
|
|
139
|
+
|
|
140
|
+
```text
|
|
141
|
+
Load Preset → /path/to/custom_preset.json
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Preset format is documented below.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Preset / Structure Format
|
|
149
|
+
|
|
150
|
+
KTrex uses a simple, declarative JSON format:
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"name": "project",
|
|
155
|
+
"type": "dir",
|
|
156
|
+
"children": [
|
|
157
|
+
{
|
|
158
|
+
"name": "src",
|
|
159
|
+
"type": "dir",
|
|
160
|
+
"children": [
|
|
161
|
+
{ "name": "main.py", "type": "file" }
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
This format is shared by:
|
|
169
|
+
|
|
170
|
+
* presets
|
|
171
|
+
* saved structures
|
|
172
|
+
* AI-generated layouts
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Safety Guarantees
|
|
177
|
+
|
|
178
|
+
KTrex is designed to be **safe by default**:
|
|
179
|
+
|
|
180
|
+
* ❌ Never overwrites files
|
|
181
|
+
* ❌ Never deletes non-empty directories
|
|
182
|
+
* ❌ Never escapes project root
|
|
183
|
+
* ✅ Undo is leaf-only and predictable
|
|
184
|
+
* ✅ All destructive actions are explicit
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
Nice question — README polish matters a lot, especially if you want your repo to look **professional + sponsor-friendly**.
|
|
189
|
+
|
|
190
|
+
Below are **clean, modern ways** to upgrade both the **License** and **Sponsor** sections without overdoing it.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 📜 License
|
|
195
|
+
|
|
196
|
+
This project is licensed under the **MIT License**.
|
|
197
|
+
You are free to use, modify, and distribute this software with proper attribution.
|
|
198
|
+
|
|
199
|
+
See the [LICENSE](./LICENSE) file for details.
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
## 💖 Sponsor
|
|
203
|
+
|
|
204
|
+
If you find this project useful and want to support its development:
|
|
205
|
+
|
|
206
|
+
[](https://ko-fi.com/akris)
|
|
207
|
+
|
|
208
|
+
Your support helps keep this project alive and actively maintained.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
KTrex.egg-info/PKG-INFO
|
|
5
|
+
KTrex.egg-info/SOURCES.txt
|
|
6
|
+
KTrex.egg-info/dependency_links.txt
|
|
7
|
+
KTrex.egg-info/entry_points.txt
|
|
8
|
+
KTrex.egg-info/top_level.txt
|
|
9
|
+
ktrex/__init__.py
|
|
10
|
+
ktrex/fs.py
|
|
11
|
+
ktrex/main.py
|
|
12
|
+
ktrex/presets.py
|
|
13
|
+
ktrex/render.py
|
|
14
|
+
ktrex/state.py
|
|
15
|
+
ktrex/tree.py
|
|
16
|
+
ktrex/tui.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ktrex
|
ktrex-0.1.5/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AK
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
ktrex-0.1.5/PKG-INFO
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: KTrex
|
|
3
|
+
Version: 0.1.5
|
|
4
|
+
Summary: Interactive terminal-based directory structure builder
|
|
5
|
+
Author: AK
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# KTrex
|
|
13
|
+
|
|
14
|
+
**KTrex** is an interactive, terminal-based directory and file structure builder.
|
|
15
|
+
It lets you visually create, navigate, undo, save, load, and reuse project layouts
|
|
16
|
+
directly from a curses-based TUI — safely and deterministically.
|
|
17
|
+
|
|
18
|
+
> Think: `mkdir` + `tree` + `undo` + `presets` + `resume`, all in one tool.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- Interactive terminal UI (arrow-key driven)
|
|
25
|
+
- Create directories and files visually
|
|
26
|
+
- Safe undo (never deletes non-empty directories)
|
|
27
|
+
- Presets (builtin or external JSON)
|
|
28
|
+
- Save project structure to JSON
|
|
29
|
+
- Load and resume saved structures
|
|
30
|
+
- Non-destructive by design (no overwrites, no forced deletes)
|
|
31
|
+
- Works offline, zero runtime dependencies
|
|
32
|
+
- Scrollable tree view (keyboard + mouse)
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
### Using pip (recommended)
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install KTrex
|
|
42
|
+
````
|
|
43
|
+
|
|
44
|
+
### Editable install (for development)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
git clone https://github.com/yourusername/KTrex.git
|
|
48
|
+
cd KTrex
|
|
49
|
+
pip install -e .
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## ▶️ Usage
|
|
55
|
+
|
|
56
|
+
Run KTrex inside any directory:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
KTrex
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
You’ll be dropped into an interactive TUI showing your project tree and available actions.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Controls
|
|
67
|
+
|
|
68
|
+
| Key | Action |
|
|
69
|
+
|-----------------------|--------------------------------|
|
|
70
|
+
| ↑ / ↓ | Navigate menu |
|
|
71
|
+
| Enter | Select highlighted action |
|
|
72
|
+
| PageUp / PageDown | Scroll tree view |
|
|
73
|
+
| Ctrl + U / Ctrl + D | Scroll tree view |
|
|
74
|
+
| Mouse Wheel | Scroll tree view |
|
|
75
|
+
| Any key | Dismiss messages / dialogs |
|
|
76
|
+
| Quit | Exit KTrex safely |
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Actions Explained
|
|
81
|
+
|
|
82
|
+
### Create Directory
|
|
83
|
+
|
|
84
|
+
* Creates a new directory in the current location
|
|
85
|
+
* Automatically enters the directory
|
|
86
|
+
|
|
87
|
+
### Create File
|
|
88
|
+
|
|
89
|
+
* Creates a file in the current directory
|
|
90
|
+
* Does **not** change navigation
|
|
91
|
+
|
|
92
|
+
### Undo
|
|
93
|
+
|
|
94
|
+
* Reverts the most recent create action
|
|
95
|
+
* Only removes files or **empty directories**
|
|
96
|
+
* Never deletes non-empty directories
|
|
97
|
+
|
|
98
|
+
### Load Preset
|
|
99
|
+
|
|
100
|
+
* Load a predefined structure by name or file path
|
|
101
|
+
* Presets are **additive**, never destructive
|
|
102
|
+
|
|
103
|
+
### Save Structure
|
|
104
|
+
|
|
105
|
+
* Serialize the entire project tree to a JSON file
|
|
106
|
+
* Useful for reuse, backups, or AI-assisted generation
|
|
107
|
+
|
|
108
|
+
### Load Structure
|
|
109
|
+
|
|
110
|
+
* Load a previously saved structure JSON
|
|
111
|
+
* Applies safely without removing existing files
|
|
112
|
+
|
|
113
|
+
### Go Back
|
|
114
|
+
|
|
115
|
+
* Navigate to parent directory
|
|
116
|
+
* Cannot escape project root
|
|
117
|
+
|
|
118
|
+
### Help
|
|
119
|
+
|
|
120
|
+
* View built-in documentation and keybindings
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Presets
|
|
125
|
+
|
|
126
|
+
### Built-in presets
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
ml_project
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Usage:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
Load Preset → ml_project
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### External presets
|
|
139
|
+
|
|
140
|
+
```text
|
|
141
|
+
Load Preset → /path/to/custom_preset.json
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Preset format is documented below.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Preset / Structure Format
|
|
149
|
+
|
|
150
|
+
KTrex uses a simple, declarative JSON format:
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"name": "project",
|
|
155
|
+
"type": "dir",
|
|
156
|
+
"children": [
|
|
157
|
+
{
|
|
158
|
+
"name": "src",
|
|
159
|
+
"type": "dir",
|
|
160
|
+
"children": [
|
|
161
|
+
{ "name": "main.py", "type": "file" }
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
This format is shared by:
|
|
169
|
+
|
|
170
|
+
* presets
|
|
171
|
+
* saved structures
|
|
172
|
+
* AI-generated layouts
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Safety Guarantees
|
|
177
|
+
|
|
178
|
+
KTrex is designed to be **safe by default**:
|
|
179
|
+
|
|
180
|
+
* ❌ Never overwrites files
|
|
181
|
+
* ❌ Never deletes non-empty directories
|
|
182
|
+
* ❌ Never escapes project root
|
|
183
|
+
* ✅ Undo is leaf-only and predictable
|
|
184
|
+
* ✅ All destructive actions are explicit
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
Nice question — README polish matters a lot, especially if you want your repo to look **professional + sponsor-friendly**.
|
|
189
|
+
|
|
190
|
+
Below are **clean, modern ways** to upgrade both the **License** and **Sponsor** sections without overdoing it.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 📜 License
|
|
195
|
+
|
|
196
|
+
This project is licensed under the **MIT License**.
|
|
197
|
+
You are free to use, modify, and distribute this software with proper attribution.
|
|
198
|
+
|
|
199
|
+
See the [LICENSE](./LICENSE) file for details.
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
## 💖 Sponsor
|
|
203
|
+
|
|
204
|
+
If you find this project useful and want to support its development:
|
|
205
|
+
|
|
206
|
+
[](https://ko-fi.com/akris)
|
|
207
|
+
|
|
208
|
+
Your support helps keep this project alive and actively maintained.
|
ktrex-0.1.5/README.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# KTrex
|
|
2
|
+
|
|
3
|
+
**KTrex** is an interactive, terminal-based directory and file structure builder.
|
|
4
|
+
It lets you visually create, navigate, undo, save, load, and reuse project layouts
|
|
5
|
+
directly from a curses-based TUI — safely and deterministically.
|
|
6
|
+
|
|
7
|
+
> Think: `mkdir` + `tree` + `undo` + `presets` + `resume`, all in one tool.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Interactive terminal UI (arrow-key driven)
|
|
14
|
+
- Create directories and files visually
|
|
15
|
+
- Safe undo (never deletes non-empty directories)
|
|
16
|
+
- Presets (builtin or external JSON)
|
|
17
|
+
- Save project structure to JSON
|
|
18
|
+
- Load and resume saved structures
|
|
19
|
+
- Non-destructive by design (no overwrites, no forced deletes)
|
|
20
|
+
- Works offline, zero runtime dependencies
|
|
21
|
+
- Scrollable tree view (keyboard + mouse)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
### Using pip (recommended)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install KTrex
|
|
31
|
+
````
|
|
32
|
+
|
|
33
|
+
### Editable install (for development)
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
git clone https://github.com/yourusername/KTrex.git
|
|
37
|
+
cd KTrex
|
|
38
|
+
pip install -e .
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## ▶️ Usage
|
|
44
|
+
|
|
45
|
+
Run KTrex inside any directory:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
KTrex
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
You’ll be dropped into an interactive TUI showing your project tree and available actions.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Controls
|
|
56
|
+
|
|
57
|
+
| Key | Action |
|
|
58
|
+
|-----------------------|--------------------------------|
|
|
59
|
+
| ↑ / ↓ | Navigate menu |
|
|
60
|
+
| Enter | Select highlighted action |
|
|
61
|
+
| PageUp / PageDown | Scroll tree view |
|
|
62
|
+
| Ctrl + U / Ctrl + D | Scroll tree view |
|
|
63
|
+
| Mouse Wheel | Scroll tree view |
|
|
64
|
+
| Any key | Dismiss messages / dialogs |
|
|
65
|
+
| Quit | Exit KTrex safely |
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Actions Explained
|
|
70
|
+
|
|
71
|
+
### Create Directory
|
|
72
|
+
|
|
73
|
+
* Creates a new directory in the current location
|
|
74
|
+
* Automatically enters the directory
|
|
75
|
+
|
|
76
|
+
### Create File
|
|
77
|
+
|
|
78
|
+
* Creates a file in the current directory
|
|
79
|
+
* Does **not** change navigation
|
|
80
|
+
|
|
81
|
+
### Undo
|
|
82
|
+
|
|
83
|
+
* Reverts the most recent create action
|
|
84
|
+
* Only removes files or **empty directories**
|
|
85
|
+
* Never deletes non-empty directories
|
|
86
|
+
|
|
87
|
+
### Load Preset
|
|
88
|
+
|
|
89
|
+
* Load a predefined structure by name or file path
|
|
90
|
+
* Presets are **additive**, never destructive
|
|
91
|
+
|
|
92
|
+
### Save Structure
|
|
93
|
+
|
|
94
|
+
* Serialize the entire project tree to a JSON file
|
|
95
|
+
* Useful for reuse, backups, or AI-assisted generation
|
|
96
|
+
|
|
97
|
+
### Load Structure
|
|
98
|
+
|
|
99
|
+
* Load a previously saved structure JSON
|
|
100
|
+
* Applies safely without removing existing files
|
|
101
|
+
|
|
102
|
+
### Go Back
|
|
103
|
+
|
|
104
|
+
* Navigate to parent directory
|
|
105
|
+
* Cannot escape project root
|
|
106
|
+
|
|
107
|
+
### Help
|
|
108
|
+
|
|
109
|
+
* View built-in documentation and keybindings
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Presets
|
|
114
|
+
|
|
115
|
+
### Built-in presets
|
|
116
|
+
|
|
117
|
+
```text
|
|
118
|
+
ml_project
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Usage:
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
Load Preset → ml_project
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### External presets
|
|
128
|
+
|
|
129
|
+
```text
|
|
130
|
+
Load Preset → /path/to/custom_preset.json
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Preset format is documented below.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Preset / Structure Format
|
|
138
|
+
|
|
139
|
+
KTrex uses a simple, declarative JSON format:
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"name": "project",
|
|
144
|
+
"type": "dir",
|
|
145
|
+
"children": [
|
|
146
|
+
{
|
|
147
|
+
"name": "src",
|
|
148
|
+
"type": "dir",
|
|
149
|
+
"children": [
|
|
150
|
+
{ "name": "main.py", "type": "file" }
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
This format is shared by:
|
|
158
|
+
|
|
159
|
+
* presets
|
|
160
|
+
* saved structures
|
|
161
|
+
* AI-generated layouts
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Safety Guarantees
|
|
166
|
+
|
|
167
|
+
KTrex is designed to be **safe by default**:
|
|
168
|
+
|
|
169
|
+
* ❌ Never overwrites files
|
|
170
|
+
* ❌ Never deletes non-empty directories
|
|
171
|
+
* ❌ Never escapes project root
|
|
172
|
+
* ✅ Undo is leaf-only and predictable
|
|
173
|
+
* ✅ All destructive actions are explicit
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
Nice question — README polish matters a lot, especially if you want your repo to look **professional + sponsor-friendly**.
|
|
178
|
+
|
|
179
|
+
Below are **clean, modern ways** to upgrade both the **License** and **Sponsor** sections without overdoing it.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 📜 License
|
|
184
|
+
|
|
185
|
+
This project is licensed under the **MIT License**.
|
|
186
|
+
You are free to use, modify, and distribute this software with proper attribution.
|
|
187
|
+
|
|
188
|
+
See the [LICENSE](./LICENSE) file for details.
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
## 💖 Sponsor
|
|
192
|
+
|
|
193
|
+
If you find this project useful and want to support its development:
|
|
194
|
+
|
|
195
|
+
[](https://ko-fi.com/akris)
|
|
196
|
+
|
|
197
|
+
Your support helps keep this project alive and actively maintained.
|
|
File without changes
|
ktrex-0.1.5/ktrex/fs.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from .state import history
|
|
3
|
+
|
|
4
|
+
def create_dir(base: Path, name: str) -> Path | None:
|
|
5
|
+
if not name:
|
|
6
|
+
return None
|
|
7
|
+
|
|
8
|
+
p = base / name
|
|
9
|
+
if p.exists():
|
|
10
|
+
return None
|
|
11
|
+
|
|
12
|
+
p.mkdir()
|
|
13
|
+
history.append(p)
|
|
14
|
+
return p
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def create_file(base: Path, name: str) -> Path | None:
|
|
18
|
+
if not name:
|
|
19
|
+
return None
|
|
20
|
+
|
|
21
|
+
p = base / name
|
|
22
|
+
if p.exists():
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
p.touch()
|
|
26
|
+
history.append(p)
|
|
27
|
+
return p
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def undo_last():
|
|
31
|
+
if not history:
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
p = history.pop()
|
|
35
|
+
|
|
36
|
+
if p.is_file():
|
|
37
|
+
p.unlink()
|
|
38
|
+
return True
|
|
39
|
+
|
|
40
|
+
if p.is_dir():
|
|
41
|
+
# SAFE UNDO: only remove empty dirs
|
|
42
|
+
if any(p.iterdir()):
|
|
43
|
+
history.append(p) # rollback undo
|
|
44
|
+
return False
|
|
45
|
+
p.rmdir()
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
return False
|
|
49
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import curses
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from KTrex import state
|
|
6
|
+
from KTrex.tui import tui_main
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def resolve_root(argv: list[str]) -> Path:
|
|
10
|
+
# Resolve project root directory from CLI arguments.
|
|
11
|
+
if len(argv) == 1:
|
|
12
|
+
# No argument → ask user
|
|
13
|
+
name = input("Project directory name (or . for current): ").strip()
|
|
14
|
+
if not name:
|
|
15
|
+
raise ValueError("Project directory name cannot be empty")
|
|
16
|
+
return Path(name)
|
|
17
|
+
|
|
18
|
+
arg = argv[1]
|
|
19
|
+
|
|
20
|
+
if arg == ".":
|
|
21
|
+
return Path.cwd()
|
|
22
|
+
|
|
23
|
+
return Path(arg)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def main():
|
|
27
|
+
try:
|
|
28
|
+
root = resolve_root(sys.argv)
|
|
29
|
+
except ValueError as e:
|
|
30
|
+
print(f"Error: {e}")
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
root = root.expanduser().resolve()
|
|
34
|
+
|
|
35
|
+
# Create directory only if it does not exist
|
|
36
|
+
root.mkdir(parents=True, exist_ok=True)
|
|
37
|
+
# Clean initialization
|
|
38
|
+
state.root_dir = root
|
|
39
|
+
state.path_stack = [root]
|
|
40
|
+
state.history.clear()
|
|
41
|
+
|
|
42
|
+
curses.wrapper(tui_main)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
main()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from .tree import apply_tree
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _builtin_preset_path(name: str) -> Path:
|
|
8
|
+
# Resolve a preset bundled inside the KTrex package.
|
|
9
|
+
|
|
10
|
+
return Path(__file__).parent / "presets" / f"{name}.json"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def load_preset(preset: str | Path, base: Path):
|
|
14
|
+
# Load a preset by name (builtin) or by explicit file path.
|
|
15
|
+
|
|
16
|
+
if isinstance(preset, str):
|
|
17
|
+
path = _builtin_preset_path(preset)
|
|
18
|
+
else:
|
|
19
|
+
path = preset
|
|
20
|
+
|
|
21
|
+
if not path.exists():
|
|
22
|
+
raise FileNotFoundError(f"Preset not found: {path}")
|
|
23
|
+
|
|
24
|
+
with open(path, "r") as f:
|
|
25
|
+
data = json.load(f)
|
|
26
|
+
|
|
27
|
+
apply_tree(base, data)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def render_tree(
|
|
5
|
+
root: Path,
|
|
6
|
+
current: Path | None = None,
|
|
7
|
+
prefix: str = "",
|
|
8
|
+
) -> list[str]:
|
|
9
|
+
"""
|
|
10
|
+
Render a directory tree as a list of strings.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
root: root directory to render
|
|
14
|
+
current: directory to highlight (optional)
|
|
15
|
+
prefix: internal prefix for recursion
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
List of rendered lines
|
|
19
|
+
"""
|
|
20
|
+
lines: list[str] = []
|
|
21
|
+
|
|
22
|
+
items = sorted(
|
|
23
|
+
root.iterdir(),
|
|
24
|
+
key=lambda x: (x.is_file(), x.name.lower()),
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
for idx, item in enumerate(items):
|
|
28
|
+
is_last = idx == len(items) - 1
|
|
29
|
+
branch = "└── " if is_last else "├── "
|
|
30
|
+
marker = " ← current" if current and item == current else ""
|
|
31
|
+
|
|
32
|
+
lines.append(f"{prefix}{branch}{item.name}{marker}")
|
|
33
|
+
|
|
34
|
+
if item.is_dir():
|
|
35
|
+
extension = " " if is_last else "│ "
|
|
36
|
+
lines.extend(
|
|
37
|
+
render_tree(
|
|
38
|
+
item,
|
|
39
|
+
current=current,
|
|
40
|
+
prefix=prefix + extension,
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
return lines
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def build_tree(path: Path) -> dict:
|
|
5
|
+
# Convert filesystem into a serializable tree structure
|
|
6
|
+
node = {"name": path.name, "type": "dir", "children": []}
|
|
7
|
+
|
|
8
|
+
for p in sorted(path.iterdir(), key=lambda x: (x.is_file(), x.name)):
|
|
9
|
+
if p.is_dir():
|
|
10
|
+
node["children"].append(build_tree(p))
|
|
11
|
+
else:
|
|
12
|
+
node["children"].append({"name": p.name, "type": "file"})
|
|
13
|
+
|
|
14
|
+
return node
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def apply_tree(base: Path, tree: dict):
|
|
18
|
+
# Create filesystem structure from a tree model
|
|
19
|
+
for child in tree.get("children", []):
|
|
20
|
+
p = base / child["name"]
|
|
21
|
+
|
|
22
|
+
if child["type"] == "dir":
|
|
23
|
+
p.mkdir(exist_ok=True)
|
|
24
|
+
apply_tree(p, child)
|
|
25
|
+
else:
|
|
26
|
+
p.touch(exist_ok=True)
|
ktrex-0.1.5/ktrex/tui.py
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import curses
|
|
2
|
+
import json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from . import state
|
|
6
|
+
from .fs import create_dir, create_file, undo_last
|
|
7
|
+
from .presets import load_preset
|
|
8
|
+
from .render import render_tree
|
|
9
|
+
from .tree import apply_tree, build_tree
|
|
10
|
+
|
|
11
|
+
MENU_ITEMS = [
|
|
12
|
+
"Create Directory",
|
|
13
|
+
"Create File",
|
|
14
|
+
"Undo",
|
|
15
|
+
"Load Preset",
|
|
16
|
+
"Save Structure",
|
|
17
|
+
"Load Structure",
|
|
18
|
+
"Go Back",
|
|
19
|
+
"Help",
|
|
20
|
+
"Quit",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
HELP_TEXT = [
|
|
24
|
+
"KTrex — Interactive Directory Builder",
|
|
25
|
+
"",
|
|
26
|
+
"Navigation:",
|
|
27
|
+
" ↑ / ↓ Move through menu",
|
|
28
|
+
" Enter Select action",
|
|
29
|
+
"",
|
|
30
|
+
"Actions:",
|
|
31
|
+
" Create Directory - Create and enter a new folder",
|
|
32
|
+
" Create File - Create a file in current directory",
|
|
33
|
+
" Undo - Revert last create action (safe)",
|
|
34
|
+
" Load Preset - Apply a preset by name or file path",
|
|
35
|
+
" Save Structure - Save project structure to JSON",
|
|
36
|
+
" Load Structure - Load structure from JSON",
|
|
37
|
+
" Go Back - Move to parent directory",
|
|
38
|
+
"",
|
|
39
|
+
"Notes:",
|
|
40
|
+
" • KTrex never deletes non-empty directories",
|
|
41
|
+
" • Existing files are never overwritten",
|
|
42
|
+
" • Presets are additive",
|
|
43
|
+
"",
|
|
44
|
+
"Scrolling:",
|
|
45
|
+
" PageUp / PageDown Scroll tree view",
|
|
46
|
+
" Ctrl + U / Ctrl + D Scroll up / down",
|
|
47
|
+
" Mouse Wheel Scroll tree",
|
|
48
|
+
"",
|
|
49
|
+
"Press any key to return",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def draw_menu(stdscr, selected: int):
|
|
54
|
+
h, w = stdscr.getmaxyx()
|
|
55
|
+
menu_width = min(24, max(10, w - 2))
|
|
56
|
+
start_x = max(0, w - menu_width)
|
|
57
|
+
start_y = 2
|
|
58
|
+
|
|
59
|
+
if start_y >= h - 1:
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
stdscr.addnstr(start_y - 1, start_x, "Actions", menu_width, curses.A_BOLD)
|
|
63
|
+
|
|
64
|
+
for i, item in enumerate(MENU_ITEMS):
|
|
65
|
+
y = start_y + i
|
|
66
|
+
if y >= h - 1:
|
|
67
|
+
break
|
|
68
|
+
attr = curses.A_REVERSE if i == selected else curses.A_NORMAL
|
|
69
|
+
stdscr.addnstr(y, start_x, item, menu_width, attr)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def draw_tree(stdscr, root: Path, current: Path, scroll_offset: int):
|
|
73
|
+
h, w = stdscr.getmaxyx()
|
|
74
|
+
stdscr.addnstr(0, 0, root.name, w - 2, curses.A_BOLD)
|
|
75
|
+
|
|
76
|
+
lines = render_tree(root, current=current)
|
|
77
|
+
|
|
78
|
+
visible_height = h - 2
|
|
79
|
+
start = scroll_offset
|
|
80
|
+
end = min(start + visible_height, len(lines))
|
|
81
|
+
|
|
82
|
+
for idx, line in enumerate(lines[start:end], start=1):
|
|
83
|
+
stdscr.addnstr(idx, 0, line, w - 2)
|
|
84
|
+
|
|
85
|
+
if len(lines) > visible_height:
|
|
86
|
+
indicator = f"[{start + 1}-{end}/{len(lines)}]"
|
|
87
|
+
stdscr.addnstr(h - 1, 0, indicator, w - 2)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def draw_help(stdscr):
|
|
91
|
+
stdscr.clear()
|
|
92
|
+
h, w = stdscr.getmaxyx()
|
|
93
|
+
|
|
94
|
+
for i, line in enumerate(HELP_TEXT):
|
|
95
|
+
y = i + 1
|
|
96
|
+
if y >= h - 1:
|
|
97
|
+
break
|
|
98
|
+
stdscr.addnstr(y, 2, line, w - 4)
|
|
99
|
+
|
|
100
|
+
stdscr.refresh()
|
|
101
|
+
stdscr.getch()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def prompt(stdscr, message: str) -> str:
|
|
105
|
+
h, w = stdscr.getmaxyx()
|
|
106
|
+
y = h - 2
|
|
107
|
+
if y < 0:
|
|
108
|
+
return ""
|
|
109
|
+
|
|
110
|
+
stdscr.move(y, 0)
|
|
111
|
+
stdscr.clrtoeol()
|
|
112
|
+
stdscr.addnstr(y, 0, message, w - 2)
|
|
113
|
+
stdscr.refresh()
|
|
114
|
+
|
|
115
|
+
curses.echo()
|
|
116
|
+
value = stdscr.getstr(y, min(len(message), w - 3)).decode().strip()
|
|
117
|
+
curses.noecho()
|
|
118
|
+
return value
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def notify(stdscr, message: str):
|
|
122
|
+
h, w = stdscr.getmaxyx()
|
|
123
|
+
y = h - 1
|
|
124
|
+
if y < 0:
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
stdscr.move(y, 0)
|
|
128
|
+
stdscr.clrtoeol()
|
|
129
|
+
stdscr.addnstr(y, 0, message, w - 2)
|
|
130
|
+
stdscr.refresh()
|
|
131
|
+
stdscr.getch()
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def tui_main(stdscr):
|
|
135
|
+
curses.curs_set(0)
|
|
136
|
+
curses.mousemask(curses.ALL_MOUSE_EVENTS)
|
|
137
|
+
stdscr.keypad(True)
|
|
138
|
+
|
|
139
|
+
scroll_offset = 0
|
|
140
|
+
root = state.root_dir
|
|
141
|
+
assert root is not None
|
|
142
|
+
selected = 0
|
|
143
|
+
|
|
144
|
+
while True:
|
|
145
|
+
current = state.path_stack[-1]
|
|
146
|
+
|
|
147
|
+
lines = render_tree(root, current=current)
|
|
148
|
+
h, _ = stdscr.getmaxyx()
|
|
149
|
+
max_offset = max(0, len(lines) - (h - 2))
|
|
150
|
+
scroll_offset = min(scroll_offset, max_offset)
|
|
151
|
+
|
|
152
|
+
stdscr.clear()
|
|
153
|
+
draw_tree(stdscr, root, current, scroll_offset)
|
|
154
|
+
draw_menu(stdscr, selected)
|
|
155
|
+
|
|
156
|
+
key = stdscr.getch()
|
|
157
|
+
|
|
158
|
+
if key == curses.KEY_RESIZE:
|
|
159
|
+
scroll_offset = 0
|
|
160
|
+
|
|
161
|
+
elif key == curses.KEY_UP and selected > 0:
|
|
162
|
+
selected -= 1
|
|
163
|
+
|
|
164
|
+
elif key == curses.KEY_DOWN and selected < len(MENU_ITEMS) - 1:
|
|
165
|
+
selected += 1
|
|
166
|
+
|
|
167
|
+
elif key == curses.KEY_NPAGE:
|
|
168
|
+
scroll_offset += 5
|
|
169
|
+
|
|
170
|
+
elif key == curses.KEY_PPAGE:
|
|
171
|
+
scroll_offset = max(0, scroll_offset - 5)
|
|
172
|
+
|
|
173
|
+
elif key == 4:
|
|
174
|
+
scroll_offset += 5
|
|
175
|
+
|
|
176
|
+
elif key == 21:
|
|
177
|
+
scroll_offset = max(0, scroll_offset - 5)
|
|
178
|
+
|
|
179
|
+
elif key == curses.KEY_MOUSE:
|
|
180
|
+
try:
|
|
181
|
+
_, _, _, _, mouse_state = curses.getmouse()
|
|
182
|
+
if mouse_state & (curses.BUTTON4_PRESSED | curses.BUTTON4_RELEASED):
|
|
183
|
+
scroll_offset = max(0, scroll_offset - 3)
|
|
184
|
+
elif mouse_state & (curses.BUTTON5_PRESSED | curses.BUTTON5_RELEASED):
|
|
185
|
+
scroll_offset += 3
|
|
186
|
+
except curses.error:
|
|
187
|
+
pass
|
|
188
|
+
|
|
189
|
+
elif key in (10, 13):
|
|
190
|
+
action = MENU_ITEMS[selected]
|
|
191
|
+
scroll_offset = 0
|
|
192
|
+
|
|
193
|
+
if action == "Create Directory":
|
|
194
|
+
name = prompt(stdscr, "Directory name: ")
|
|
195
|
+
if name:
|
|
196
|
+
new_dir = create_dir(current, name)
|
|
197
|
+
if new_dir:
|
|
198
|
+
state.path_stack.append(new_dir)
|
|
199
|
+
|
|
200
|
+
elif action == "Create File":
|
|
201
|
+
name = prompt(stdscr, "File name: ")
|
|
202
|
+
if name:
|
|
203
|
+
create_file(current, name)
|
|
204
|
+
|
|
205
|
+
elif action == "Undo":
|
|
206
|
+
undone = undo_last()
|
|
207
|
+
if undone and not current.exists():
|
|
208
|
+
state.path_stack.pop()
|
|
209
|
+
|
|
210
|
+
elif action == "Load Preset":
|
|
211
|
+
value = prompt(stdscr, "Preset name or path: ")
|
|
212
|
+
if value:
|
|
213
|
+
try:
|
|
214
|
+
load_preset(value, current)
|
|
215
|
+
except Exception as e:
|
|
216
|
+
notify(stdscr, f"Error: {e}")
|
|
217
|
+
|
|
218
|
+
elif action == "Save Structure":
|
|
219
|
+
path_str = prompt(stdscr, "Save to (path): ")
|
|
220
|
+
if path_str:
|
|
221
|
+
try:
|
|
222
|
+
tree = build_tree(root)
|
|
223
|
+
with open(Path(path_str), "w") as f:
|
|
224
|
+
json.dump(tree, f, indent=2)
|
|
225
|
+
notify(stdscr, "Structure saved successfully")
|
|
226
|
+
except Exception as e:
|
|
227
|
+
notify(stdscr, f"Error: {e}")
|
|
228
|
+
|
|
229
|
+
elif action == "Load Structure":
|
|
230
|
+
path_str = prompt(stdscr, "Load from (path): ")
|
|
231
|
+
if path_str:
|
|
232
|
+
try:
|
|
233
|
+
with open(Path(path_str), "r") as f:
|
|
234
|
+
tree = json.load(f)
|
|
235
|
+
apply_tree(root, tree)
|
|
236
|
+
except Exception as e:
|
|
237
|
+
notify(stdscr, f"Error: {e}")
|
|
238
|
+
|
|
239
|
+
elif action == "Go Back":
|
|
240
|
+
if len(state.path_stack) > 1:
|
|
241
|
+
state.path_stack.pop()
|
|
242
|
+
else:
|
|
243
|
+
notify(stdscr, "Already at project root")
|
|
244
|
+
|
|
245
|
+
elif action == "Help":
|
|
246
|
+
draw_help(stdscr)
|
|
247
|
+
|
|
248
|
+
elif action == "Quit":
|
|
249
|
+
break
|
|
250
|
+
|
|
251
|
+
stdscr.refresh()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "KTrex"
|
|
3
|
+
version = "0.1.5"
|
|
4
|
+
description = "Interactive terminal-based directory structure builder"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { text = "MIT" }
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "AK" }
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
dependencies = []
|
|
12
|
+
|
|
13
|
+
[project.scripts]
|
|
14
|
+
KTrex = "KTrex.main:main"
|
|
15
|
+
|
|
16
|
+
[tool.setuptools.package-data]
|
|
17
|
+
KTrex = ["presets/*.json"]
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["setuptools>=61"]
|
|
21
|
+
build-backend = "setuptools.build_meta"
|
ktrex-0.1.5/setup.cfg
ADDED