src/bedit/
├── __main__.py
├── core/ # Pure Python, no PySide
│ ├── model.py
│ ├── port_types.py
│ ├── serializer.py
│ └── libraries.py
└── gui/ # All Qt-dependent code
├── app.py
├── main_window.py
├── preferences.py
├── controllers/
├── dialogs/
├── graphics/
├── models/
└── generated/ # Designer/resource output only
Notable improvements:
Domain models, serialization, port types, and library parsing are now Qt-free.
Qt signals and undo infrastructure are explicitly isolated under gui/controllers.
Library parsing is separated from the Qt repository and tree models.
All generated Python is contained in gui/generated.
Designer build tasks now write to the generated directory.
The application entry point and package metadata use the new paths.
README now documents the structure and dependency rules.
Removed the old mixed document, library, and workspace packages.
7.5 KiB
AGENTS.md
This file is the working guide for AI coding agents contributing to BEdit. Its instructions apply to the entire repository.
Project purpose
BEdit is a Python/PySide6 graphical editor for hierarchical block and bond-graph models. A document contains reusable components, typed and oriented ports, connections, nested graphs, and editable vector icons.
The application is evolving quickly. Prefer small, coherent changes that improve the architecture without introducing compatibility layers unless compatibility is explicitly requested.
Architecture
The main boundary is strict:
src/bedit/
├── __main__.py
├── core/ # Pure Python; must not import PySide6
│ ├── model.py # Document, component, graph, port, icon data
│ ├── port_types.py # Port type definitions and compatibility
│ ├── serializer.py # JSON persistence
│ └── libraries.py # Library file discovery and parsing
└── gui/ # All Qt-dependent code
├── app.py # QApplication startup and palette
├── main_window.py # Top-level UI orchestration
├── preferences.py # Explicit disk-backed QSettings factory
├── controllers/ # Document controller and undo commands
├── dialogs/ # Dialog behavior
├── graphics/ # Workspace, icon editor, vector rendering
├── models/ # Qt tree models and repository adapters
└── generated/ # Generated UI/resource Python; never hand-edit
Dependency direction:
bedit.coremay depend only on the Python standard library.bedit.guimay depend onbedit.coreand PySide6.bedit.coremust never importbedit.guior PySide6.- Data parsing and validation belong in
core. - Signals, widgets, painting, Qt models, undo integration, and settings UI belong
in
gui.
Keep the root of src/bedit minimal. New UI modules should go into the relevant
gui subpackage rather than the package root.
Important behavior and design decisions
- Components may contain nested graph or text implementations.
- Ports retain stable IDs. Display names, types, orientation, positions, and icon anchors may change without changing IDs.
- Port orientation is presented as one unified list in the UI, while the model indexes inputs and outputs separately for connection semantics.
- Port types are registered in
core/port_types.py. Only compatible types may be connected.signalis currently the only type. - Port removal or reorientation must be rejected when it would invalidate an existing connection.
- Connections reference port IDs, never port names.
- Icon editing uses a fixed 128×128 coordinate space.
- The visible/selectable component hitbox is calculated from vector elements, not from the complete 128×128 icon canvas.
- New component icons default to a centered 64×64 shape.
- Vector elements include rectangles, circles, ellipses, lines, triangles, and text. Shape styling and geometry are stored in document JSON.
- Icon port anchors are stored in
Port.properties["iconPosition"]. - Workspace grid size, workspace snapping size, and icon grid size are separate persisted settings.
- Settings use
gui.preferences.application_settings()and are stored under the explicitBEdit/BEditidentity. Do not create anonymousQSettings()objects. - Avoid the QSettings group name
general; Qt treatsGeneralspecially in INI files. Autosave keys live underautosave/. - User-visible document edits should participate in undo/redo.
Qt Designer and generated files
Editable Designer sources are in ui/. Resources are defined in
resources/resources.qrc.
Never hand-edit files in src/bedit/gui/generated/. Modify the corresponding
.ui or .qrc source and regenerate instead.
Use the configured VS Code task Qt: Build Designer Files, or run the relevant commands directly:
pyside6-rcc resources/resources.qrc \
-o src/bedit/gui/generated/resources_rc.py
pyside6-uic --from-imports ui/main_window.ui \
-o src/bedit/gui/generated/ui_main_window.py
pyside6-uic --from-imports ui/settings_dialog.ui \
-o src/bedit/gui/generated/ui_settings_dialog.py
pyside6-uic --from-imports ui/component_options_dialog.ui \
-o src/bedit/gui/generated/ui_component_options_dialog.py
When adding a promoted/custom widget in Designer, its header must use the real
Python module path, for example bedit.gui.graphics.workspace.
Some dialogs are currently assembled programmatically. Keep that code inside
gui/dialogs; do not put widget creation in core.
Editing conventions
- Preserve stable document IDs and existing connections.
- Validate candidate document changes before pushing an undo command.
- Keep model serialization symmetrical: additions to
to_dict()require matching handling infrom_dict()and cloning where relevant. - Use descriptive domain names; avoid generic
utils.pymodules. - Prefer focused classes and helpers over growing
main_window.pyfurther. - Shared vector calculations that do not require Qt belong in
core; Qt painter code belongs ingui/graphics. - Do not silently swallow malformed document data. Raise a useful
ValueErrorincore, then present it through the GUI layer. - Preserve unrelated user changes. The worktree may already be dirty.
- Do not delete or overwrite library/document JSON unless the requested workflow explicitly calls for it.
Document and library files
- Documents use the
bedit-documentJSON format. test.bedit.jsonis a useful manually created example during development.- Library documents use the same recursive document model.
- Library parsing belongs in
core/libraries.py; Qt change notifications belong ingui/models/library_repository.py. - Package library data, if present, belongs under
src/bedit/data/libraries/.
Validation
There is not yet a complete automated test suite. For every change, run at least:
python3 -m compileall -q src
git diff --check
Run imports with the source tree explicitly available:
PYTHONPATH=src python3 -c "import bedit.core; import bedit.gui.app"
Verify the backend remains Qt-free after core changes:
PYTHONPATH=src python3 - <<'PY'
import sys
import bedit.core
assert not any(name.startswith("PySide6") for name in sys.modules)
PY
For model changes, add a focused in-memory round-trip check using
GraphDocument.to_dict() and GraphDocument.from_dict(). For controller changes,
exercise undo and redo when applicable.
GUI smoke tests may fail in headless environments because the system Qt platform theme tries to access a display even with an offscreen platform. Do not claim an interactive GUI test passed unless a display-capable environment was actually used. Compilation, imports, and pure model/controller checks remain useful.
If Ruff is installed, also run:
ruff check src
Running the application
Install the package in editable mode or run it from the source tree:
PYTHONPATH=src python3 -m bedit
The installed GUI entry point is bedit.gui.app:main.
Completion checklist
Before handing off a change:
- Confirm the
core/guidependency boundary is intact. - Confirm generated files were not hand-edited.
- Check serialization and cloning for model changes.
- Check undo/redo for document mutations.
- Run compilation and
git diff --check. - Report any GUI behavior that could not be tested interactively.
- Update README or this file if the architecture or workflow changed.