Skip to content

Commit 92b3272

Browse files
author
Lukas Geiger
committed
fix: support CodeBox startup file arguments
1 parent 68fa1f8 commit 92b3272

7 files changed

Lines changed: 149 additions & 23 deletions

File tree

.github/workflows/linux-platform-smoke.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ jobs:
1515
PYTHONIOENCODING: utf-8
1616
steps:
1717
- name: Checkout
18-
uses: actions/checkout@v4
18+
uses: actions/checkout@v6
1919

2020
- name: Setup Python
21-
uses: actions/setup-python@v5
21+
uses: actions/setup-python@v6
2222
with:
2323
python-version: "3.12"
2424

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,8 @@ DIAGNOSE_REPORT.md
6262
SKILL.md
6363
tmpclaude-*
6464
.claude/
65+
# ---- Interne Steuerungsdateien ----
66+
DECISIONS.md
67+
PORTIERUNGSPLAN.md
68+
TODO.md
69+
DONE.md

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@ Format basiert auf [Keep a Changelog](https://keepachangelog.com/de/1.1.0/).
55

66
## [Unreleased]
77

8+
### Build / Release
9+
- EXE aktualisiert 2026-06-01 (OneDrive-Lock aufgelöst nach Beenden alter Prozesse); enthält Startup-/CLI-Bug-Fix (`--open`-Argument + offener Bootstrap-Tab). 13/13 Tests grün, Smoke OK.
10+
- EXE neu gebaut 2026-06-01 (PyInstaller, `CodeBox.spec``C:\_Local_DEV\codex_build\codebox`); 11/12 Tests grün (1 skipped), Smoke-Test bestanden. Vorherige EXE: 2026-05-28.
11+
812
### Hinzugefügt
13+
- Linux-Source-Smoke für offscreen App-Start, Dateiöffnung, Terminalpfad,
14+
Projektbaum-`xdg-open` und lokale Python-Run-Commands.
15+
- Regressionstest für Startup-Dateiübergabe per `--open` und positionalem Pfad.
16+
- README-Discoverability für GitHub/Web-Suche geschärft: englischer SEO-Einstieg,
17+
CodeBox-Namenskollision erklärt, Quickstart und präzisere Suchbegriffe ergänzt.
918
- Headless-Smoke-Test für MainWindow-Instanziierung
1019
- Optionale LSP-Runtime-Tests für `python-lsp-server[all]`:
1120
Diagnostics bei Syntaxfehlern und Completion über `pylsp`.
@@ -15,6 +24,8 @@ Format basiert auf [Keep a Changelog](https://keepachangelog.com/de/1.1.0/).
1524
beim Tippen an den aktiven LSP-Client geschickt.
1625

1726
### Behoben
27+
- `python main.py --open <datei>` und nackte Dateipfade öffnen jetzt die Datei
28+
direkt beim Start und entfernen den leeren Bootstrap-Tab.
1829
- `QApplication` fehlte im Import von `ui/main_window.py` (wurde in Theme-Lambda verwendet)
1930
- Diverse ungenutzte Imports entfernt (core, features, languages, ui)
2031
- Fenstertitel liest die Version jetzt aus `version.py` statt aus einem Hardcode

README.md

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
1-
# CodeBox
1+
# CodeBox - local PySide6 desktop code editor
22

3-
Mehrsprachiger Desktop-Codeeditor auf Basis von PySide6.
4-
Multi-language desktop code editor built with PySide6.
3+
CodeBox is a local-first desktop IDE for Windows developers who want a
4+
lightweight PySide6 code editor with tabs, a project tree, an integrated
5+
terminal, Git helpers, syntax highlighting and Language Server Protocol
6+
diagnostics.
57

6-
CodeBox ist aus PythonBox v8 hervorgegangen und bündelt Editor, Projektbaum,
7-
Terminal sowie erste LSP- und API-Grundlagen in einer lokalen IDE.
8+
Mehrsprachiger Desktop-Codeeditor auf Basis von PySide6. CodeBox ist aus
9+
PythonBox v8 hervorgegangen und bündelt Editor, Projektbaum, Terminal sowie
10+
erste LSP- und API-Grundlagen in einer lokalen IDE.
11+
12+
## Why CodeBox
13+
14+
- Local-first: edit files on your machine without cloud accounts or telemetry.
15+
- PySide6 desktop stack: native Windows app behavior with a small Python codebase.
16+
- Multi-language workflow: Python, JavaScript, TypeScript, C++, Rust, Go and Java.
17+
- LSP-ready: diagnostics and completion can connect to installed language servers.
18+
- Dev-bricks ecosystem: companion to PythonBox and DevCenter for small local tools.
819

920
## Screenshot
1021

@@ -26,10 +37,20 @@ Terminal sowie erste LSP- und API-Grundlagen in einer lokalen IDE.
2637
```bash
2738
pip install -r requirements.txt
2839
python main.py
40+
python main.py --open path/to/file.py
2941
```
3042

3143
Alternativ per Doppelklick auf `start.bat`.
3244

45+
### Quick start
46+
47+
1. Clone `https://github.com/dev-bricks/CodeBox`.
48+
2. Install dependencies with `pip install -r requirements.txt`.
49+
3. Run `python main.py`.
50+
4. Optional: install language servers such as `python-lsp-server[all]` or
51+
`typescript-language-server` for diagnostics and completion.
52+
5. Open a file directly with `python main.py --open path/to/file.py`.
53+
3354
### Voraussetzungen / Requirements
3455

3556
- Python 3.10+
@@ -86,6 +107,14 @@ config/ Konfigurationsdateien
86107
themes/ QSS-Themes
87108
```
88109

110+
## Discovery keywords
111+
112+
CodeBox is best described as a local PySide6 code editor, Windows desktop IDE,
113+
offline code editor, LSP-enabled Python editor and lightweight multi-language
114+
developer tool. The repository name collides with older projects called
115+
`codebox`, so searches are most precise with `dev-bricks CodeBox`,
116+
`CodeBox PySide6`, `CodeBox LSP editor` or `file-bricks/dev-bricks desktop IDE`.
117+
89118
## Status
90119

91120
Aktueller Stand: `DEV`, Version `0.1.0`

main.py

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Author: Lukas
88
"""
99

10+
import argparse
1011
import sys
1112
from pathlib import Path
1213

@@ -24,22 +25,54 @@ def load_app_icon() -> QIcon:
2425
return QIcon(str(icon_path)) if icon_path.exists() else QIcon()
2526

2627

27-
def main():
28+
def parse_launch_args(argv=None):
29+
"""Extract startup file arguments and leave unknown Qt args untouched."""
30+
parser = argparse.ArgumentParser(add_help=False)
31+
parser.add_argument("--open", dest="open_path")
32+
args, qt_args = parser.parse_known_args(list(argv or []))
33+
34+
startup_path = Path(args.open_path).expanduser() if args.open_path else None
35+
if startup_path is None and qt_args and not qt_args[0].startswith("-"):
36+
startup_path = Path(qt_args[0]).expanduser()
37+
qt_args = qt_args[1:]
38+
return startup_path, qt_args
39+
40+
41+
def _should_replace_startup_placeholder(window) -> bool:
42+
"""Detect the initial empty tab created during window bootstrap."""
43+
tab = window.tab_widget.current_tab()
44+
return bool(
45+
tab
46+
and window.tab_widget.count() == 1
47+
and tab.file_path is None
48+
and not tab.is_modified
49+
and not tab.editor.toPlainText()
50+
)
51+
52+
53+
def main(argv=None):
2854
"""Entry point for CodeBox."""
29-
app = QApplication(sys.argv)
55+
startup_path, qt_args = parse_launch_args(sys.argv[1:] if argv is None else argv)
56+
57+
app = QApplication.instance() or QApplication([sys.argv[0], *qt_args])
3058
icon = load_app_icon()
3159
if not icon.isNull():
3260
app.setWindowIcon(icon)
3361
apply_theme(app, DEFAULT_THEME)
3462

3563
from ui.main_window import MainWindow
3664
window = MainWindow()
65+
replace_placeholder = _should_replace_startup_placeholder(window)
3766
if not icon.isNull():
3867
window.setWindowIcon(icon)
68+
if startup_path:
69+
opened_tab = window.open_path(startup_path)
70+
if opened_tab and replace_placeholder and window.tab_widget.count() > 1:
71+
window.tab_widget.close_tab(0)
3972
window.show()
4073

41-
sys.exit(app.exec())
74+
return app.exec()
4275

4376

4477
if __name__ == "__main__":
45-
main()
78+
sys.exit(main())

tests/test_startup_args.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from unittest.mock import patch
2+
3+
import main
4+
5+
6+
def test_parse_launch_args_supports_open_flag_and_positional(tmp_path):
7+
target = tmp_path / "script.py"
8+
9+
startup_path, qt_args = main.parse_launch_args(
10+
["--open", str(target), "--platform", "offscreen"]
11+
)
12+
assert startup_path == target
13+
assert qt_args == ["--platform", "offscreen"]
14+
15+
startup_path, qt_args = main.parse_launch_args([str(target)])
16+
assert startup_path == target
17+
assert qt_args == []
18+
19+
20+
def test_main_opens_startup_file_from_open_flag(tmp_path):
21+
target = tmp_path / "script.py"
22+
target.write_text("print('hi')\n", encoding="utf-8")
23+
captured = []
24+
25+
with (
26+
patch("features.terminal.TerminalWidget._start_shell", lambda self: None),
27+
patch("ui.main_window.MainWindow.show", lambda self: captured.append(self)),
28+
patch("PySide6.QtWidgets.QApplication.exec", return_value=0),
29+
):
30+
exit_code = main.main(["--open", str(target)])
31+
32+
assert exit_code == 0
33+
assert len(captured) == 1
34+
35+
window = captured[0]
36+
try:
37+
assert window.tab_widget.count() == 1
38+
tab = window.tab_widget.current_tab()
39+
assert tab is not None
40+
assert tab.file_path == target
41+
assert window.tab_widget.tabText(window.tab_widget.currentIndex()) == "script.py"
42+
finally:
43+
window.close()

ui/main_window.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,22 @@ def open_file(self):
160160
"Alle Dateien (*);;Python (*.py);;JavaScript (*.js);;C++ (*.cpp *.h)"
161161
)
162162
if path:
163-
tab = self.tab_widget.open_file(Path(path))
164-
if tab and tab.provider:
165-
self.lang_label.setText(tab.provider.get_name())
166-
self.output.run_btn.setEnabled(True)
167-
self._connect_lsp(tab, Path(path))
168-
self._connect_cursor(tab)
163+
self.open_path(Path(path))
164+
165+
def open_path(self, file_path: Path):
166+
"""Öffnet einen konkreten Pfad ohne Dateidialog."""
167+
path = Path(file_path)
168+
if not path.exists():
169+
QMessageBox.warning(self, "Datei öffnen", f"Datei nicht gefunden:\n{path}")
170+
return None
171+
172+
tab = self.tab_widget.open_file(path)
173+
if tab and tab.provider:
174+
self.lang_label.setText(tab.provider.get_name())
175+
self.output.run_btn.setEnabled(True)
176+
self._connect_lsp(tab, path)
177+
self._connect_cursor(tab)
178+
return tab
169179

170180
def _connect_lsp(self, tab, file_path: Path):
171181
"""Verbindet den Tab mit dem LSP-Server für die Sprache."""
@@ -437,12 +447,7 @@ def _connect_cursor(self, tab):
437447

438448
def _open_file_from_project(self, file_path):
439449
"""Öffnet eine Datei aus dem Projektbaum."""
440-
tab = self.tab_widget.open_file(file_path)
441-
if tab and tab.provider:
442-
self.lang_label.setText(tab.provider.get_name())
443-
self.output.run_btn.setEnabled(True)
444-
self._connect_lsp(tab, file_path)
445-
self._connect_cursor(tab)
450+
self.open_path(file_path)
446451

447452
def _toggle_project_view(self):
448453
"""Blendet den Projektbaum ein/aus."""

0 commit comments

Comments
 (0)