Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# PyBreeze

Automation-first Python IDE built on PySide6 + JEditor, integrating Web/API/GUI/Load testing into a single environment.

## Architecture

**Layered architecture with Facade + Strategy patterns:**

```
pybreeze/
├── __init__.py # Public API facade (start_editor, plugin re-exports)
├── pybreeze_ui/ # Presentation layer (PySide6 widgets)
│ ├── editor_main/ # Main window (extends JEditor)
│ ├── menu/ # Menu bar builders (automation, install, tools, plugins)
│ ├── connect_gui/ssh/ # SSH client widgets
│ ├── extend_ai_gui/ # LLM code review & prompt editors
│ ├── jupyter_lab_gui/ # JupyterLab tab integration
│ ├── syntax/ # Automation keyword highlighting definitions
│ └── show_code_window/ # CodeWindow - output display widget
├── extend/
│ ├── process_executor/ # Process isolation layer (Strategy pattern)
│ │ ├── python_task_process_manager.py # Core: TaskProcessManager (subprocess + thread + QTimer)
│ │ ├── process_executor_utils.py # Factory functions: build_process / start_process
│ │ ├── file_runner_process.py # FileRunnerProcess for plugin run configs
│ │ ├── api_testka/ # Each module delegates to build_process with its package name
│ │ ├── auto_control/
│ │ ├── web_runner/
│ │ ├── load_density/
│ │ ├── file_automation/
│ │ ├── mail_thunder/
│ │ └── test_pioneer/ # TestPioneerProcess (custom variant)
│ └── mail_thunder_extend/ # Post-test email report hook
├── extend_multi_language/ # Built-in i18n (English, Traditional Chinese)
└── utils/
├── exception/ # Exception hierarchy (ITEException base)
├── logging/ # pybreeze_logger
├── file_process/ # File/directory utilities
├── json_format/ # JSON processing
└── manager/package_manager/ # PackageManager class
```

**Key design patterns in use:**
- **Facade**: `pybreeze/__init__.py` exposes `start_editor()`, `EDITOR_EXTEND_TAB`, and plugin APIs
- **Strategy**: Each automation module (`api_testka`, `web_runner`, etc.) is a strategy that delegates to `TaskProcessManager` via `build_process()`
- **Template Method**: `TaskProcessManager` defines the subprocess lifecycle (start -> read stdout/stderr threads -> QTimer poll -> drain -> exit)
- **Observer**: QTimer-based polling bridges subprocess output to PySide6 UI thread via thread-safe Queues
- **Plugin System**: Auto-discovery from `jeditor_plugins/` directory; plugins register via `register()` function

## Key types

- `PyBreezeMainWindow` — main window class (extends JEditor), holds `tab_widget` and `current_run_code_window`
- `TaskProcessManager` — core process executor; manages subprocess, I/O threads, and QTimer UI updates
- `CodeWindow` — output display widget passed to `TaskProcessManager`
- `PackageManager` — pip wrapper for installing automation modules
- `EDITOR_EXTEND_TAB: dict` — registry for custom tabs (key=name, value=QWidget subclass)

## Branching & CI

- `main` branch: stable releases, publishes `pybreeze` to PyPI
- `dev` branch: development, publishes `pybreeze_dev` to PyPI
- Version config: `pyproject.toml` (stable), `dev.toml` (dev) — keep both in sync when bumping
- CI runs on GitHub Actions (Windows, Python 3.10/3.11/3.12)
- CI steps: install deps -> pytest `test/test_utils/` -> start_automation_test -> extend_automation_test

## Development

```bash
python -m pip install -r dev_requirements.txt
python -m pytest test/test_utils/ -v --tb=short
python -m pybreeze # launch the IDE
```

**Testing:**
- Unit tests: `test/test_utils/` (pure logic: exceptions, JSON, logger, file utils, package manager, venv path, jupyter helpers)
- Integration tests: `test/unit_test/start_automation/` (launches IDE in debug_mode, verifies startup and extend tab)
- Run all tests before submitting changes: `python -m pytest test/test_utils/ -v`

## Conventions

- Python 3.10+ — use `X | Y` union syntax, not `Union[X, Y]`
- Use `from __future__ import annotations` for deferred type evaluation
- Use `TYPE_CHECKING` guard for imports only needed by type hints (avoid circular imports)
- PySide6 threading: never update UI from worker threads — use Queue + QTimer pattern (see `TaskProcessManager`)
- Exception hierarchy: all custom exceptions inherit from `ITEException`
- Logging: use `pybreeze_logger` from `pybreeze.utils.logging.logger`
- Plugin API: `register_programming_language()` and `register_natural_language()` from `je_editor.plugins`
- Delete all unused code — do not leave dead imports, unreachable functions, commented-out blocks, or unused variables. If code is not called by any execution path, remove it entirely. No `# TODO: remove later` or `_old_` prefixes — delete immediately.

## Security

All code must follow secure-by-default principles. Review every change against the checklist below before committing.

### General rules
- Never use `eval()`, `exec()`, or `pickle.loads()` on untrusted data
- Never use `subprocess.Popen(..., shell=True)` — always pass argument lists
- Never log or display secrets, tokens, passwords, or API keys
- Use `json.loads()` / `json.dumps()` for serialisation — never pickle
- Validate all user input at system boundaries (file dialogs, URL inputs, network data)

### Network requests (SSRF prevention)
- All outbound HTTP requests must go through `diagram_net_utils.safe_download_image()` or equivalent guards
- Only `http://` and `https://` schemes are allowed — block `file://`, `ftp://`, `data:`, `gopher://`
- Resolved IP addresses must be checked against private/loopback/link-local ranges (`ipaddress.is_private`, `is_loopback`, `is_link_local`, `is_reserved`)
- Enforce download size limits (default: 20 MB) and connection timeouts (default: 15s)
- Never pass user-supplied URLs directly to `urlopen()` without validation

### File I/O
- File read/write paths from user dialogs (`QFileDialog`) are trusted (user-initiated)
- File paths loaded from saved data (`.diagram.json`) must be validated before access:
- Local paths: check `path.is_file()` and verify extension is in an allowlist
- URLs: pass through the same SSRF validation as user-entered URLs
- Never construct file paths by string concatenation with user input — use `pathlib.Path` with validation

### Qt / UI
- `QGraphicsTextItem` with `TextEditorInteraction` must not be enabled by default — use double-click-to-edit pattern to prevent unintended text selection issues in themed environments
- Plugin loading (`jeditor_plugins/`) uses auto-discovery — only load `.py` files, skip files starting with `_` or `.`

## Commit & PR rules

- Commit messages: short imperative sentence (e.g., "Update stable version", "Fix github actions")
- Do not mention any AI tools, assistants, or co-authors in commit messages or PR descriptions
- Do not add `Co-Authored-By` headers referencing any AI
- PR target: `dev` for development work, `main` for stable releases
4 changes: 2 additions & 2 deletions exe/auto_py_to_exe_setting.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@
},
{
"optionDest": "collect_all",
"value": "ipython"
"value": "debugpy"
},
{
"optionDest": "collect_all",
"value": "debugpy"
"value": "ipython"
}
],
"nonPyinstallerOptions": {
Expand Down
97 changes: 97 additions & 0 deletions pybreeze/extend_multi_language/extend_english.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,103 @@
"file_tree_ctx_already_exists": "'{name}' already exists.",
"file_tree_ctx_confirm_delete": "Confirm Delete",
"file_tree_ctx_confirm_delete_message": "Are you sure you want to delete '{name}'?",
# Diagram Editor — Menu
"extend_tools_menu_diagram_editor_tab_action": "Diagram Editor Tab",
"extend_tools_menu_diagram_editor_tab_label": "Diagram Editor",
"extend_tools_menu_diagram_editor_dock_action": "Diagram Editor Dock",
"extend_tools_menu_diagram_editor_dock_title": "Diagram Editor",
# Diagram Editor — Tools
"diagram_editor_tool_select": "Select",
"diagram_editor_tool_rect": "Rect",
"diagram_editor_tool_rounded_rect": "Rounded",
"diagram_editor_tool_ellipse": "Ellipse",
"diagram_editor_tool_diamond": "Diamond",
"diagram_editor_tool_connection": "Connect",
"diagram_editor_tool_text": "Text",
# Diagram Editor — Actions
"diagram_editor_action_new": "New",
"diagram_editor_action_open": "Open",
"diagram_editor_action_save": "Save",
"diagram_editor_action_save_as": "Save As",
"diagram_editor_action_import": "Import",
"diagram_editor_action_export_png": "PNG",
"diagram_editor_action_export_svg": "SVG",
"diagram_editor_action_zoom_fit": "Fit",
"diagram_editor_action_undo": "Undo",
"diagram_editor_action_redo": "Redo",
"diagram_editor_action_grid": "Grid",
"diagram_editor_action_snap": "Snap",
# Diagram Editor — Align
"diagram_editor_align_menu": "Align",
"diagram_editor_align_left": "Align Left",
"diagram_editor_align_right": "Align Right",
"diagram_editor_align_top": "Align Top",
"diagram_editor_align_bottom": "Align Bottom",
"diagram_editor_align_center_h": "Center Horizontal",
"diagram_editor_align_center_v": "Center Vertical",
"diagram_editor_distribute_h": "Distribute Horizontal",
"diagram_editor_distribute_v": "Distribute Vertical",
# Diagram Editor — Dialogs
"diagram_editor_confirm_title": "Confirm",
"diagram_editor_confirm_new": "Discard current diagram?",
"diagram_editor_dialog_open": "Open Diagram",
"diagram_editor_dialog_save": "Save Diagram",
"diagram_editor_dialog_export_png": "Export PNG",
"diagram_editor_dialog_export_svg": "Export SVG",
"diagram_editor_error_title": "Error",
# Diagram Editor — Property Panel
"diagram_editor_prop_title": "Properties",
"diagram_editor_prop_no_selection": "No selection",
"diagram_editor_prop_node_group": "Node",
"diagram_editor_prop_conn_group": "Connection",
"diagram_editor_prop_text": "Text",
"diagram_editor_prop_width": "Width",
"diagram_editor_prop_height": "Height",
"diagram_editor_prop_shape": "Shape",
"diagram_editor_prop_fill_color": "Fill",
"diagram_editor_prop_border_color": "Border",
"diagram_editor_prop_font_size": "Font Size",
"diagram_editor_prop_label": "Label",
"diagram_editor_prop_line_style": "Style",
"diagram_editor_prop_line_color": "Color",
"diagram_editor_prop_line_width": "Width",
# Diagram Editor — Shape / Style names
"diagram_editor_shape_rectangle": "Rectangle",
"diagram_editor_shape_rounded_rect": "Rounded Rect",
"diagram_editor_shape_ellipse": "Ellipse",
"diagram_editor_shape_diamond": "Diamond",
"diagram_editor_style_solid": "Solid",
"diagram_editor_style_dashed": "Dashed",
"diagram_editor_style_dotted": "Dotted",
# Diagram Editor — Context Menu
"diagram_editor_ctx_delete": "Delete",
"diagram_editor_ctx_duplicate": "Duplicate",
"diagram_editor_ctx_bring_front": "Bring to Front",
"diagram_editor_ctx_send_back": "Send to Back",
"diagram_editor_ctx_select_all": "Select All",
"diagram_editor_ctx_paste": "Paste",
# Diagram Editor — Image
"diagram_editor_tool_image_file": "Image",
"diagram_editor_tool_image_url": "URL Image",
"diagram_editor_dialog_image_file": "Open Image",
"diagram_editor_dialog_image_url": "Image URL",
"diagram_editor_dialog_image_url_hint": "Enter image URL:",
"diagram_editor_image_load_failed": "Failed to load image.",
"diagram_editor_prop_img_group": "Image",
"diagram_editor_prop_caption": "Caption",
"diagram_editor_prop_source": "Source",
# Diagram Editor — Mermaid Import
"diagram_editor_import_title": "Import Mermaid Diagram",
"diagram_editor_import_hint": "Paste Mermaid flowchart code below:",
"diagram_editor_import_convert": "Convert",
"diagram_editor_import_cancel": "Cancel",
"diagram_editor_import_error": "Parse Error",
"diagram_editor_import_empty": "No nodes found in the input.",
# Diagram Editor — Status Bar
"diagram_editor_status_select": "Click to select, drag to move. Right-click to pan.",
"diagram_editor_status_add_node": "Click canvas to place a node.",
"diagram_editor_status_connection": "Click source node, then click target node.",
"diagram_editor_status_text": "Click canvas to place a text node.",
# Plugin Browser
"plugin_browser_tab_name": "Plugin Browser",
"plugin_browser_repo_label": "Repository URL:",
Expand Down
97 changes: 97 additions & 0 deletions pybreeze/extend_multi_language/extend_traditional_chinese.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,103 @@
"jupyterlab_loading": "載入中...",
"jupyterlab_timeout": "JupyterLab 啟動超時",
"jupyterlab_init_failed": "JupyterLab 啟動失敗",
# Diagram Editor — 選單
"extend_tools_menu_diagram_editor_tab_action": "架構圖編輯器分頁",
"extend_tools_menu_diagram_editor_tab_label": "架構圖編輯器",
"extend_tools_menu_diagram_editor_dock_action": "架構圖編輯器停駐窗格",
"extend_tools_menu_diagram_editor_dock_title": "架構圖編輯器",
# Diagram Editor — 工具
"diagram_editor_tool_select": "選取",
"diagram_editor_tool_rect": "矩形",
"diagram_editor_tool_rounded_rect": "圓角",
"diagram_editor_tool_ellipse": "橢圓",
"diagram_editor_tool_diamond": "菱形",
"diagram_editor_tool_connection": "連線",
"diagram_editor_tool_text": "文字",
# Diagram Editor — 動作
"diagram_editor_action_new": "新增",
"diagram_editor_action_open": "開啟",
"diagram_editor_action_save": "儲存",
"diagram_editor_action_save_as": "另存新檔",
"diagram_editor_action_import": "匯入",
"diagram_editor_action_export_png": "PNG",
"diagram_editor_action_export_svg": "SVG",
"diagram_editor_action_zoom_fit": "全覽",
"diagram_editor_action_undo": "復原",
"diagram_editor_action_redo": "重做",
"diagram_editor_action_grid": "格線",
"diagram_editor_action_snap": "對齊",
# Diagram Editor — 對齊
"diagram_editor_align_menu": "對齊",
"diagram_editor_align_left": "靠左對齊",
"diagram_editor_align_right": "靠右對齊",
"diagram_editor_align_top": "靠上對齊",
"diagram_editor_align_bottom": "靠下對齊",
"diagram_editor_align_center_h": "水平置中",
"diagram_editor_align_center_v": "垂直置中",
"diagram_editor_distribute_h": "水平均分",
"diagram_editor_distribute_v": "垂直均分",
# Diagram Editor — 對話框
"diagram_editor_confirm_title": "確認",
"diagram_editor_confirm_new": "是否捨棄目前的架構圖?",
"diagram_editor_dialog_open": "開啟架構圖",
"diagram_editor_dialog_save": "儲存架構圖",
"diagram_editor_dialog_export_png": "匯出 PNG",
"diagram_editor_dialog_export_svg": "匯出 SVG",
"diagram_editor_error_title": "錯誤",
# Diagram Editor — 屬性面板
"diagram_editor_prop_title": "屬性",
"diagram_editor_prop_no_selection": "未選取",
"diagram_editor_prop_node_group": "節點",
"diagram_editor_prop_conn_group": "連線",
"diagram_editor_prop_text": "文字",
"diagram_editor_prop_width": "寬度",
"diagram_editor_prop_height": "高度",
"diagram_editor_prop_shape": "形狀",
"diagram_editor_prop_fill_color": "填充",
"diagram_editor_prop_border_color": "邊框",
"diagram_editor_prop_font_size": "字體大小",
"diagram_editor_prop_label": "標籤",
"diagram_editor_prop_line_style": "樣式",
"diagram_editor_prop_line_color": "顏色",
"diagram_editor_prop_line_width": "寬度",
# Diagram Editor — 形狀 / 樣式名稱
"diagram_editor_shape_rectangle": "矩形",
"diagram_editor_shape_rounded_rect": "圓角矩形",
"diagram_editor_shape_ellipse": "橢圓",
"diagram_editor_shape_diamond": "菱形",
"diagram_editor_style_solid": "實線",
"diagram_editor_style_dashed": "虛線",
"diagram_editor_style_dotted": "點線",
# Diagram Editor — 右鍵選單
"diagram_editor_ctx_delete": "刪除",
"diagram_editor_ctx_duplicate": "複製",
"diagram_editor_ctx_bring_front": "移到最前",
"diagram_editor_ctx_send_back": "移到最後",
"diagram_editor_ctx_select_all": "全選",
"diagram_editor_ctx_paste": "貼上",
# Diagram Editor — 圖片
"diagram_editor_tool_image_file": "圖片",
"diagram_editor_tool_image_url": "網路圖片",
"diagram_editor_dialog_image_file": "開啟圖片",
"diagram_editor_dialog_image_url": "圖片網址",
"diagram_editor_dialog_image_url_hint": "輸入圖片網址:",
"diagram_editor_image_load_failed": "無法載入圖片。",
"diagram_editor_prop_img_group": "圖片",
"diagram_editor_prop_caption": "標題",
"diagram_editor_prop_source": "來源",
# Diagram Editor — Mermaid 匯入
"diagram_editor_import_title": "匯入 Mermaid 架構圖",
"diagram_editor_import_hint": "在下方貼上 Mermaid 流程圖程式碼:",
"diagram_editor_import_convert": "轉換",
"diagram_editor_import_cancel": "取消",
"diagram_editor_import_error": "解析錯誤",
"diagram_editor_import_empty": "輸入中找不到任何節點。",
# Diagram Editor — 狀態列
"diagram_editor_status_select": "點擊選取,拖曳移動。右鍵平移畫布。",
"diagram_editor_status_add_node": "點擊畫布放置節點。",
"diagram_editor_status_connection": "點擊來源節點,再點擊目標節點。",
"diagram_editor_status_text": "點擊畫布放置文字節點。",
# File Tree Context Menu
"file_tree_ctx_new_file": "新增檔案",
"file_tree_ctx_new_folder": "新增資料夾",
Expand Down
Empty file.
28 changes: 28 additions & 0 deletions pybreeze/pybreeze_ui/diagram_editor/diagram_commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from PySide6.QtGui import QUndoCommand

if TYPE_CHECKING:
from pybreeze.pybreeze_ui.diagram_editor.diagram_scene import DiagramScene


class DiagramSnapshotCommand(QUndoCommand):
"""Snapshot-based undo/redo: stores full scene state before and after a change."""

def __init__(self, scene: DiagramScene, description: str, old_data: dict, new_data: dict):
super().__init__(description)
self._scene = scene
self._old_data = old_data
self._new_data = new_data
self._first_redo = True

def redo(self) -> None:
if self._first_redo:
self._first_redo = False
return
self._scene._restore_from_dict(self._new_data)

def undo(self) -> None:
self._scene._restore_from_dict(self._old_data)
Loading
Loading