211 lines
8.4 KiB
Markdown
211 lines
8.4 KiB
Markdown
# 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:
|
||
|
||
```text
|
||
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.core` may depend only on the Python standard library.
|
||
- `bedit.gui` may depend on `bedit.core` and PySide6.
|
||
- `bedit.core` must never import `bedit.gui` or 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. `signal` is 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.
|
||
- Graph interaction has separate Pointer and Connect modes. Connections store a
|
||
`properties.routing` value (`direct`, `angled`, or `spline`); angled routes
|
||
store absolute scene points in `properties.waypoints`.
|
||
- Connection appearance is configured per port type in
|
||
`gui/graphics/connection_styles.py`, including color, width, pen style, and
|
||
source/target arrowheads. Do not scatter those constants through painters.
|
||
- 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
|
||
explicit `BEdit/BEdit` identity. Do not create anonymous `QSettings()` objects.
|
||
- Avoid the QSettings group name `general`; Qt treats `General` specially in INI
|
||
files. Autosave keys live under `autosave/`.
|
||
- 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:
|
||
|
||
```bash
|
||
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
|
||
|
||
pyside6-uic --from-imports ui/port_options_dialog.ui \
|
||
-o src/bedit/gui/generated/ui_port_options_dialog.py
|
||
|
||
pyside6-uic --from-imports ui/shape_options_dialog.ui \
|
||
-o src/bedit/gui/generated/ui_shape_options_dialog.py
|
||
|
||
pyside6-uic --from-imports ui/icon_editor_dialog.ui \
|
||
-o src/bedit/gui/generated/ui_icon_editor_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`.
|
||
|
||
Substantial windows and dialogs must have a Designer `.ui` source. Python classes
|
||
bind behavior and data but must not reconstruct or replace those layouts at
|
||
runtime. A tiny generic prompt with one field and OK/Cancel may remain code-only.
|
||
|
||
## 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 in `from_dict()` and cloning where relevant.
|
||
- Use descriptive domain names; avoid generic `utils.py` modules.
|
||
- Prefer focused classes and helpers over growing `main_window.py` further.
|
||
- Shared vector calculations that do not require Qt belong in `core`; Qt painter
|
||
code belongs in `gui/graphics`.
|
||
- Do not silently swallow malformed document data. Raise a useful `ValueError` in
|
||
`core`, 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-document` JSON format.
|
||
- `test.bedit.json` is 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
|
||
in `gui/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:
|
||
|
||
```bash
|
||
python3 -m compileall -q src
|
||
git diff --check
|
||
```
|
||
|
||
Run imports with the source tree explicitly available:
|
||
|
||
```bash
|
||
PYTHONPATH=src python3 -c "import bedit.core; import bedit.gui.app"
|
||
```
|
||
|
||
Verify the backend remains Qt-free after core changes:
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
ruff check src
|
||
```
|
||
|
||
## Running the application
|
||
|
||
Install the package in editable mode or run it from the source tree:
|
||
|
||
```bash
|
||
PYTHONPATH=src python3 -m bedit
|
||
```
|
||
|
||
The installed GUI entry point is `bedit.gui.app:main`.
|
||
|
||
## Completion checklist
|
||
|
||
Before handing off a change:
|
||
|
||
1. Confirm the `core`/`gui` dependency boundary is intact.
|
||
2. Confirm generated files were not hand-edited.
|
||
3. Check serialization and cloning for model changes.
|
||
4. Check undo/redo for document mutations.
|
||
5. Run compilation and `git diff --check`.
|
||
6. Report any GUI behavior that could not be tested interactively.
|
||
7. Update README or this file if the architecture or workflow changed.
|