tracedsa 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. tracedsa/__init__.py +0 -0
  2. tracedsa/__main__.py +404 -0
  3. tracedsa/bins/linux/bst +0 -0
  4. tracedsa/bins/linux/circqueue +0 -0
  5. tracedsa/bins/linux/dll +0 -0
  6. tracedsa/bins/linux/heap +0 -0
  7. tracedsa/bins/linux/ll +0 -0
  8. tracedsa/bins/linux/queue +0 -0
  9. tracedsa/bins/linux/queuell +0 -0
  10. tracedsa/bins/linux/stack +0 -0
  11. tracedsa/bins/linux/stackll +0 -0
  12. tracedsa/bins/macos/bst +0 -0
  13. tracedsa/bins/macos/circqueue +0 -0
  14. tracedsa/bins/macos/dll +0 -0
  15. tracedsa/bins/macos/heap +0 -0
  16. tracedsa/bins/macos/ll +0 -0
  17. tracedsa/bins/macos/queue +0 -0
  18. tracedsa/bins/macos/queuell +0 -0
  19. tracedsa/bins/macos/stack +0 -0
  20. tracedsa/bins/macos/stackll +0 -0
  21. tracedsa/bins/windows/bst.exe +0 -0
  22. tracedsa/bins/windows/circqueue.exe +0 -0
  23. tracedsa/bins/windows/dll.exe +0 -0
  24. tracedsa/bins/windows/heap.exe +0 -0
  25. tracedsa/bins/windows/ll.exe +0 -0
  26. tracedsa/bins/windows/queue.exe +0 -0
  27. tracedsa/bins/windows/queuell.exe +0 -0
  28. tracedsa/bins/windows/stack.exe +0 -0
  29. tracedsa/bins/windows/stackll.exe +0 -0
  30. tracedsa/bridge.py +74 -0
  31. tracedsa/screens/__init__.py +0 -0
  32. tracedsa/screens/confirm_dialog.py +113 -0
  33. tracedsa/screens/help_screen.py +80 -0
  34. tracedsa/screens/info_screen.py +531 -0
  35. tracedsa/screens/menu.py +475 -0
  36. tracedsa/screens/splash.py +214 -0
  37. tracedsa/screens/trace_screen.py +518 -0
  38. tracedsa/widgets/__init__.py +0 -0
  39. tracedsa/widgets/ascii_array.py +103 -0
  40. tracedsa/widgets/ascii_heap.py +73 -0
  41. tracedsa/widgets/ascii_tree.py +75 -0
  42. tracedsa/widgets/ops_log.py +30 -0
  43. tracedsa-1.0.0.dist-info/METADATA +81 -0
  44. tracedsa-1.0.0.dist-info/RECORD +47 -0
  45. tracedsa-1.0.0.dist-info/WHEEL +4 -0
  46. tracedsa-1.0.0.dist-info/entry_points.txt +2 -0
  47. tracedsa-1.0.0.dist-info/licenses/LICENSE +21 -0
tracedsa/__init__.py ADDED
File without changes
tracedsa/__main__.py ADDED
@@ -0,0 +1,404 @@
1
+ #!/usr/bin/env python3
2
+ from textual.app import App, ComposeResult
3
+ from textual.binding import Binding
4
+
5
+ from tracedsa.screens.splash import SplashScreen
6
+ from tracedsa.screens.confirm_dialog import ConfirmDialog
7
+
8
+ import platform
9
+ import os
10
+
11
+ from tracedsa.bridge import DSBridge
12
+
13
+
14
+ SHORTCUTS = {
15
+ "global": [
16
+ ("q", "Quit (with confirmation)", "All screens"),
17
+ ("h / ?", "Show this help screen", "All screens"),
18
+ ],
19
+ "splash": [
20
+ ("enter / s", "Start application", "SplashScreen"),
21
+ ],
22
+ "menu": [
23
+ ("/", "Focus search bar to filter modules", "MainMenu"),
24
+ ("escape", "Quit (with confirmation)", "MainMenu"),
25
+ ],
26
+ "trace": [
27
+ ("escape", "Return to main menu", "TraceWindow"),
28
+ ("Tab", "Cycle through buttons and inputs", "TraceWindow"),
29
+ ],
30
+ }
31
+
32
+
33
+ class TraceDSApp(App):
34
+ CSS_PATH = None
35
+
36
+ SHORTCUTS = SHORTCUTS
37
+
38
+ DEFAULT_CSS = """
39
+ /* === App & Screen === */
40
+ App {
41
+ background: #1a1a2e;
42
+ color: #e0e0e0;
43
+ }
44
+
45
+ Screen {
46
+ background: #1a1a2e;
47
+ height: 100%;
48
+ }
49
+
50
+ Static, Label {
51
+ color: #e0e0e0;
52
+ }
53
+
54
+ Container {
55
+ background: transparent;
56
+ }
57
+
58
+ /* === Main Menu === */
59
+ #main_menu_container {
60
+ layout: vertical;
61
+ width: 100%;
62
+ height: 100%;
63
+ }
64
+
65
+ #header-section {
66
+ layout: vertical;
67
+ align: center middle;
68
+ width: 100%;
69
+ height: auto;
70
+ background: #16213e;
71
+ border: round #0f3460;
72
+ padding: 1 2;
73
+ margin-bottom: 1;
74
+ }
75
+
76
+ #title {
77
+ color: #ffffff;
78
+ text-style: bold;
79
+ text-align: center;
80
+ width: 100%;
81
+ }
82
+
83
+ #search_input {
84
+ width: 40;
85
+ margin: 1 0;
86
+ background: #1a1a2e;
87
+ border: round #0f3460;
88
+ color: #e0e0e0;
89
+ }
90
+
91
+ #search_input:focus {
92
+ border: round #00d4ff;
93
+ }
94
+
95
+ #search_input > .input--placeholder {
96
+ color: #666680;
97
+ }
98
+
99
+ #middle_section {
100
+ layout: vertical;
101
+ align: center middle;
102
+ width: 100%;
103
+ height: auto;
104
+ background: #16213e;
105
+ border: round #0f3460;
106
+ padding: 1 2;
107
+ margin-bottom: 1;
108
+ }
109
+
110
+ #random_art {
111
+ color: #00d4ff;
112
+ text-align: center;
113
+ width: 100%;
114
+ }
115
+
116
+ #fun_fact {
117
+ color: #e0e0e0;
118
+ text-align: center;
119
+ text-style: italic;
120
+ width: 100%;
121
+ margin-top: 1;
122
+ }
123
+
124
+ #button_section {
125
+ layout: vertical;
126
+ align: center middle;
127
+ width: 100%;
128
+ height: 1fr;
129
+ padding: 1 2;
130
+ overflow-y: auto;
131
+ overflow-x: hidden;
132
+ }
133
+
134
+ #button_section Horizontal {
135
+ margin-bottom: 1;
136
+ }
137
+
138
+ /* === Buttons === */
139
+ Button {
140
+ background: #16213e;
141
+ color: #e0e0e0;
142
+ border: round #0f3460;
143
+ text-style: bold;
144
+ }
145
+
146
+ Button:hover {
147
+ background: #00d4ff;
148
+ color: #1a1a2e;
149
+ }
150
+
151
+ Button:focus {
152
+ border: round #00d4ff;
153
+ text-style: bold underline;
154
+ }
155
+
156
+ Button.primary {
157
+ color: #00d4ff;
158
+ border: round #00d4ff;
159
+ }
160
+
161
+ Button.primary:hover {
162
+ background: #00d4ff;
163
+ color: #1a1a2e;
164
+ }
165
+
166
+ Button.success {
167
+ color: #ffffff;
168
+ border: round #0f3460;
169
+ }
170
+
171
+ Button.success:hover {
172
+ background: #00d4ff;
173
+ color: #1a1a2e;
174
+ }
175
+
176
+ Button.default {
177
+ color: #666680;
178
+ border: round #0f3460;
179
+ }
180
+
181
+ Button.default:hover {
182
+ color: #e0e0e0;
183
+ border: round #00d4ff;
184
+ }
185
+
186
+ /* === Trace Window === */
187
+ #trace_window_container {
188
+ layout: vertical;
189
+ width: 100%;
190
+ height: 100%;
191
+ }
192
+
193
+ #trace_header {
194
+ layout: horizontal;
195
+ content-align: left middle;
196
+ width: 100%;
197
+ height: auto;
198
+ background: #16213e;
199
+ border: round #0f3460;
200
+ padding: 0 2;
201
+ }
202
+
203
+ #trace_header #back_button {
204
+ width: auto;
205
+ color: #666680;
206
+ }
207
+
208
+ #trace_header #back_button:hover {
209
+ color: #00d4ff;
210
+ }
211
+
212
+ #module_name {
213
+ color: #ffffff;
214
+ text-style: bold;
215
+ text-align: center;
216
+ width: 1fr;
217
+ }
218
+
219
+ #main_content {
220
+ layout: horizontal;
221
+ width: 100%;
222
+ height: 1fr;
223
+ }
224
+
225
+ #ascii_panel {
226
+ width: 60%;
227
+ height: 100%;
228
+ background: #16213e;
229
+ border: round #0f3460;
230
+ padding: 1 2;
231
+ overflow: auto;
232
+ }
233
+
234
+ #ascii_placeholder {
235
+ color: #666680;
236
+ text-align: center;
237
+ }
238
+
239
+ #ascii_panel Static {
240
+ width: 100%;
241
+ height: auto;
242
+ }
243
+
244
+ ASCIIArray, ASCIIBranchTree, ASCIIHeap {
245
+ width: 100%;
246
+ height: 100%;
247
+ }
248
+
249
+ #log_panel {
250
+ width: 40%;
251
+ height: 100%;
252
+ background: #16213e;
253
+ border: round #0f3460;
254
+ layout: vertical;
255
+ padding: 0 1;
256
+ }
257
+
258
+ #log_header {
259
+ color: #00d4ff;
260
+ text-style: bold;
261
+ width: 100%;
262
+ text-align: center;
263
+ padding: 1 0;
264
+ }
265
+
266
+ #log_panel RichLog {
267
+ background: #1a1a2e;
268
+ color: #e0e0e0;
269
+ width: 100%;
270
+ height: 1fr;
271
+ }
272
+
273
+ OpsLog {
274
+ width: 100%;
275
+ height: 1fr;
276
+ }
277
+
278
+ #button_container {
279
+ width: 100%;
280
+ height: auto;
281
+ background: #16213e;
282
+ border: round #0f3460;
283
+ padding: 1 2;
284
+ layout: horizontal;
285
+ overflow-x: auto;
286
+ }
287
+
288
+ #button_container Horizontal {
289
+ width: auto;
290
+ margin-right: 1;
291
+ }
292
+
293
+ #button_container Button {
294
+ margin: 0 1;
295
+ }
296
+
297
+ .clear-log {
298
+ margin-left: 3;
299
+ }
300
+
301
+ /* === Status Bar === */
302
+ #status_bar {
303
+ color: #666680;
304
+ text-style: italic;
305
+ width: 100%;
306
+ height: auto;
307
+ padding: 0 2;
308
+ background: #16213e;
309
+ border-top: solid #0f3460;
310
+ }
311
+
312
+ /* === Inputs === */
313
+ Input {
314
+ background: #1a1a2e;
315
+ color: #e0e0e0;
316
+ border: round #0f3460;
317
+ width: 12;
318
+ margin-right: 1;
319
+ }
320
+
321
+ Input:focus {
322
+ border: round #00d4ff;
323
+ }
324
+
325
+ Input > .input--placeholder {
326
+ color: #666680;
327
+ }
328
+
329
+ #button_container Input {
330
+ width: 12;
331
+ max-width: 16;
332
+ margin: 0 1;
333
+ }
334
+
335
+ /* === Scrollbar === */
336
+ ScrollBar {
337
+ background: #1a1a2e;
338
+ }
339
+
340
+ ScrollBar:hover {
341
+ background: #16213e;
342
+ }
343
+
344
+ ScrollBar > .scrollbar--thumb {
345
+ background: #00d4ff;
346
+ }
347
+
348
+ ScrollBar > .scrollbar--thumb:hover {
349
+ background: #ffffff;
350
+ }
351
+ """
352
+
353
+ TITLE = "TraceDSA"
354
+ SUB_TITLE = "Data Structures Visualization"
355
+
356
+ BINDINGS = [
357
+ Binding("q", "show_confirm_quit", "Quit", priority=True),
358
+ Binding("TAB", "cycle_focus", "Cycle Focus")
359
+ ]
360
+
361
+ def __init__(self):
362
+ super().__init__()
363
+ self.bridges = {}
364
+
365
+ def initialize_bridge(self, name: str) -> bool:
366
+ sysname = platform.system().lower()
367
+ binary_path = f"tracedsa/bins/{sysname}/{name}"
368
+ if os.path.exists(binary_path):
369
+ try:
370
+ self.bridges[name] = DSBridge(name)
371
+ return True
372
+ except Exception:
373
+ return False
374
+ return False
375
+
376
+ def get_bridge(self, name: str):
377
+ return self.bridges.get(name)
378
+
379
+ def compose(self) -> ComposeResult:
380
+ yield from []
381
+
382
+ def on_mount(self) -> None:
383
+ self.push_screen(SplashScreen())
384
+
385
+ def action_show_confirm_quit(self) -> None:
386
+ from tracedsa.screens.confirm_dialog import ConfirmDialog
387
+ if isinstance(self.screen, ConfirmDialog):
388
+ self.exit()
389
+ else:
390
+ self.push_screen(ConfirmDialog())
391
+
392
+ def action_quit(self) -> None:
393
+ for bridge in self.bridges.values():
394
+ bridge.close()
395
+ self.exit()
396
+
397
+
398
+ def main():
399
+ app = TraceDSApp()
400
+ app.run()
401
+
402
+
403
+ if __name__ == "__main__":
404
+ main()
Binary file
Binary file
Binary file
Binary file
tracedsa/bins/linux/ll ADDED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
tracedsa/bins/macos/ll ADDED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
tracedsa/bridge.py ADDED
@@ -0,0 +1,74 @@
1
+ import subprocess
2
+ import platform
3
+ import os
4
+
5
+ def get_binary(name):
6
+ system = platform.system().lower()
7
+ ext = ".exe" if system == "windows" else ""
8
+ folder = "macos" if system == "darwin" else system
9
+ base = os.path.dirname(__file__)
10
+ return os.path.join(base, "bins", folder, f"{name}{ext}")
11
+
12
+ class DSBridge:
13
+ def __init__(self, name):
14
+ self.name = name
15
+ self.proc = subprocess.Popen(
16
+ [get_binary(name)],
17
+ stdin=subprocess.PIPE,
18
+ stdout=subprocess.PIPE,
19
+ stderr=subprocess.PIPE,
20
+ text=True
21
+ )
22
+ ready_line = self.proc.stdout.readline().strip()
23
+ if not ready_line:
24
+ stderr_tail = self._read_stderr()
25
+ raise RuntimeError(
26
+ f"Binary '{name}' produced no output (crashed?). "
27
+ f"Stderr: {stderr_tail or '(empty)'}"
28
+ )
29
+ if ready_line != "READY":
30
+ raise RuntimeError(f"Expected READY from '{name}', got: {ready_line}")
31
+
32
+ def _read_stderr(self):
33
+ try:
34
+ return self.proc.stderr.read().strip()
35
+ except Exception:
36
+ return None
37
+
38
+ def is_alive(self) -> bool:
39
+ return self.proc.poll() is None
40
+
41
+ def send(self, command):
42
+ if not self.is_alive():
43
+ raise RuntimeError("Process terminated")
44
+
45
+ try:
46
+ self.proc.stdin.write(command + "\n")
47
+ self.proc.stdin.flush()
48
+ except (BrokenPipeError, OSError) as e:
49
+ raise RuntimeError(f"Failed to send command: {e}")
50
+
51
+ try:
52
+ response = self.proc.stdout.readline()
53
+ except (ValueError, OSError) as e:
54
+ raise RuntimeError(f"Failed to read response: {e}")
55
+
56
+ if not response:
57
+ stderr_tail = self._read_stderr()
58
+ detail = f" (stderr: {stderr_tail})" if stderr_tail else ""
59
+ raise RuntimeError(f"Binary '{self.name}' closed stdout — likely crashed{detail}")
60
+
61
+ return response.strip()
62
+
63
+ def close(self):
64
+ try:
65
+ if self.is_alive():
66
+ self.proc.stdin.write("EXIT\n")
67
+ self.proc.stdin.flush()
68
+ except (BrokenPipeError, OSError):
69
+ pass
70
+ finally:
71
+ try:
72
+ self.proc.terminate()
73
+ except ProcessLookupError:
74
+ pass
File without changes
@@ -0,0 +1,113 @@
1
+ from textual.app import ComposeResult
2
+ from textual.containers import Container, Horizontal
3
+ from textual.widgets import Static, Button
4
+ from textual.screen import Screen
5
+ from textual.binding import Binding
6
+
7
+
8
+ class ConfirmDialog(Screen):
9
+
10
+ DEFAULT_CSS = """
11
+ ConfirmDialog {
12
+ align: center middle;
13
+ background: rgba(0, 0, 0, 0.7);
14
+ }
15
+
16
+ #confirm-box {
17
+ width: 44;
18
+ height: auto;
19
+ padding: 2 4;
20
+ background: #16213e;
21
+ border: round #00d4ff;
22
+ align: center middle;
23
+ }
24
+
25
+ #confirm-title {
26
+ text-align: center;
27
+ width: 100%;
28
+ color: #ffffff;
29
+ text-style: bold;
30
+ margin-bottom: 1;
31
+ }
32
+
33
+ #confirm-subtitle {
34
+ text-align: center;
35
+ width: 100%;
36
+ color: #666680;
37
+ margin-bottom: 1;
38
+ }
39
+
40
+ #confirm-buttons {
41
+ align: center middle;
42
+ width: 100%;
43
+ height: auto;
44
+ margin-top: 1;
45
+ }
46
+
47
+ #confirm-y {
48
+ background: #16213e;
49
+ border: round #00d4ff;
50
+ color: #00d4ff;
51
+ width: 1;
52
+ margin-right: 1;
53
+ }
54
+
55
+ #confirm-y:hover {
56
+ background: #00d4ff;
57
+ color: #1a1a2e;
58
+ }
59
+
60
+ #confirm-n {
61
+ background: #16213e;
62
+ border: round #666680;
63
+ color: #666680;
64
+ width: 1;
65
+ }
66
+
67
+ #confirm-n:hover {
68
+ background: #666680;
69
+ color: #1a1a2e;
70
+ }
71
+ """
72
+
73
+ BINDINGS = [
74
+ Binding("y", "confirm", "Yes"),
75
+ Binding("n", "cancel", "No"),
76
+ Binding("escape", "cancel", "Cancel"),
77
+ ]
78
+
79
+ def __init__(self, title: str = "Exit TraceDSA?", message: str = "All unsaved state will be lost.", on_confirm = None):
80
+ super().__init__()
81
+ self._title = title
82
+ self._message = message
83
+ self._on_confirm = on_confirm
84
+
85
+ def compose(self) -> ComposeResult:
86
+ yield Container(
87
+ Static(self._title, id="confirm-title"),
88
+ Static(self._message, id="confirm-subtitle"),
89
+ Horizontal(
90
+ Button("Yes [y]", id="confirm-y"),
91
+ Button("No [n]", id="confirm-n"),
92
+ id="confirm-buttons"
93
+ ),
94
+ id="confirm-box"
95
+ )
96
+
97
+ def action_confirm(self) -> None:
98
+ if self._on_confirm:
99
+ self._on_confirm()
100
+ else:
101
+ self.app.exit()
102
+
103
+ def action_cancel(self) -> None:
104
+ self.app.pop_screen()
105
+
106
+ def on_button_pressed(self, event: Button.Pressed) -> None:
107
+ if event.button.id == "confirm-y":
108
+ if self._on_confirm:
109
+ self._on_confirm()
110
+ else:
111
+ self.app.exit()
112
+ else:
113
+ self.app.pop_screen()