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.
@@ -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
+ [![Ko-Fi](https://img.shields.io/badge/☕%20Buy%20Me%20a%20Coffee-ko--fi-red)](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,2 @@
1
+ [console_scripts]
2
+ KTrex = KTrex.main:main
@@ -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
+ [![Ko-Fi](https://img.shields.io/badge/☕%20Buy%20Me%20a%20Coffee-ko--fi-red)](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
+ [![Ko-Fi](https://img.shields.io/badge/☕%20Buy%20Me%20a%20Coffee-ko--fi-red)](https://ko-fi.com/akris)
196
+
197
+ Your support helps keep this project alive and actively maintained.
File without changes
@@ -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,11 @@
1
+ from pathlib import Path
2
+
3
+ # Root directory of the session
4
+ root_dir: Path | None = None
5
+
6
+ # Stack-based navigation (no recursion)
7
+ path_stack: list[Path] = []
8
+
9
+ # Undo stack (only last-created leaf nodes)
10
+ history: list[Path] = []
11
+
@@ -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)
@@ -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
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+