diff --git a/.parcel_backups.db b/.parcel_backups.db
new file mode 100644
index 0000000..6ae9e24
Binary files /dev/null and b/.parcel_backups.db differ
diff --git a/docs/complete_changelog.md b/docs/complete_changelog.md
deleted file mode 100644
index 5d77fb9..0000000
--- a/docs/complete_changelog.md
+++ /dev/null
@@ -1,57 +0,0 @@
-# Complete Changelog & Migration Record: Parcel-Python
-
-This document records all architectural implementations, migrations, features, and refinements performed during the creation of **Parcel-Python**, converted from the original C++ (Qt 6) codebase.
-
----
-
-## 1. Migration Overview (C++ to Python)
-- **Framework Stack**: Migrated from C++17 / Qt 6 Widgets / QML to **Python 3.10+ / PyQt6 / PyQt6-WebEngine**.
-- **Modular Package Structure**: Organized inside the `parcel/` package with dedicated submodules for core, services, AI agents, and views.
-
----
-
-## 2. Core Layer & Navigation (`parcel/core/`)
-- **`NavigationController`**: Thread-safe singleton managing view transitions via `NavigationTarget` enums (`HOME`, `NEW_PROJECT`, `OPEN_PROJECT`, `SETTINGS`, `WORKSPACE`).
-- **Constants & Version**: Global application constants and versioning (`1.0.0`).
-
----
-
-## 3. Native Service & Backup Layer (`parcel/service/`)
-- **`PersistenceService`**: Settings manager with XOR-based encryption (`0xAF`) for LLM API keys (Gemini/OpenAI) and usage telemetry tracking.
-- **`BackupService`**: SQLite database snapshotter (`.parcel_backups.db`) managing recursive build backups and individual file revision saves.
-- **`ExportService`**: Native Python export engine capable of generating Debian packages (`.deb`), release tarballs (`.tar.gz`), and AppImage directory structures inside the `build/` folder.
-- **`GitService` & Desktop Managers**: Subprocess Git repository detector, branch tracker, and environment bridges for KDE, GTK (2/3/4), .NET, PHP, and Node.js.
-
----
-
-## 4. SS AI Agent Subsystem (`parcel/ss_ai_agent/`)
-- **`QLMEngine`**: Evaluates natural language instructions against JSON rule files (`QLM/` and `SS QLM/`).
-- **`LocalAgent` & `ProjectKnowledge`**: Recursively indexes project symbols (classes and functions), manages short-term memory, and tracks technical debt.
-- **`FilePatcher`**: Transactional code patcher ensuring safe, atomic file modifications (`reconstruct`, `safe_create`, `patch`, `smart_append`).
-- **`ScriptValidator`**: Shell script linter enforcing SSQLM safety standards (`set -uo pipefail`, error traps, unquoted variable checks).
-- **`MLService` & `WebSearch`**: Python ML context predictor and Trafilatura web research agent.
-
----
-
-## 5. View Layer & UI Components (`parcel/view/`)
-- **Cyber-Dark Theme (`theme.py`)**: Deep space color palette applied via custom `QPalette` and stylesheets.
-- **Navigation Views**:
- - `DashboardView`: Welcome dashboard with New Project and Open Project buttons.
- - `NewProjectView`: Multi-step project creation wizard.
- - `OpenProjectView`: Folder picker and static project analysis report viewer.
- - `SettingsView`: API key configuration and framework integrations view.
-- **Project Header & Workspace (`project_header.py`, `project_workspace.py`)**:
- - Compact header with Git branch badge and action buttons: **East Pane**, **South Pane**, **Designer**, **Build + Backup** (triggers SQLite backup and live terminal build logging), and **Export** (menu with `.deb`, `.tar.gz`, `.AppDir` options).
-- **Advanced Syntax Highlighter (`highlighter.py`)**:
- - Supports **Python, C/C++, Java, C#, VB.NET, F#, Go, Rust, JavaScript, TypeScript, PHP, Ruby, Perl, Raku, ASP, JSP, Assembly, Shell Script, SQL, JSON, YAML, HTML/XML/QML, and Markdown**.
-- **Interactive QML Designer (`designer_pane.py`, `DesignerCanvas`)**:
- - Component palette with 30+ QML & Kirigami elements, interactive mouse-draggable canvas for precise X/Y positioning, Properties Inspector, and live QML Code Generator.
-- **Assistant & Tool Panes**:
- - Gemini, OpenAI, Terminal, Logcat, Browser, Databases, Inspection, Versions, and Saves panes.
-
----
-
-## 6. Automation, Packaging & Execution
-- **`build.sh` & `run.sh`**: Automated environment setup and execution scripts (available at project root and inside `build/`).
-- **Dynamic Application Icon**: Automatically generates and saves the "PP" monogram icon with Python branding colors (`resources/parcel-python-dark.png` and `~/.local/share/icons/`).
-- **`.gitignore`**: Configured to exclude virtual environments, build artifacts, and large SQLite backup files (`.parcel_backups.db`).
diff --git a/docs/index.html b/docs/index.html
deleted file mode 100644
index 6365d18..0000000
--- a/docs/index.html
+++ /dev/null
@@ -1,180 +0,0 @@
-
-
-
-
-
- Parcel-Python — Manual de Uso & Documentação
-
-
-
-
-
-
-
-
-
- 1. Introdução ao Software
- O Parcel-Python é um Ambiente de Desenvolvimento Integrado (IDE) avançado e Toolkit Industrial desenvolvido em Python 3.10+ com PyQt6. Ele integra inteligência artificial on-device (SS AI Agent), versionamento incremental em SQLite (.parcel_backups.db), designer visual QML interativo e realce de sintaxe profissional para mais de 15 linguagens.
-
-
-
- 2. Instalação & Execução Rápida
- Utilize os scripts de automação fornecidos na raiz do projeto para configurar o ambiente virtual e rodar a IDE:
- # 1. Configurar dependências e ambiente virtual
-./build.sh
-
-# 2. Iniciar a IDE
-./run.sh
- Ou manualmente via terminal:
- python3 -m venv venv
-source venv/bin/activate
-pip install -r requirements.txt
-python3 -m parcel.main
-
-
-
- 3. Navegação & Telas Iniciais
-
- - Dashboard: Tela de boas-vindas com acesso rápido para criar novos projetos ou abrir projetos existentes.
- - New Project Wizard: Assistente estruturado em etapas (Projeto, Linguagem, Framework, Dependências, Git e Sumário).
- - Open Project Analyzer: Seleciona pastas de projeto e realiza análises estáticas (detecção de Git, contagem de arquivos, tamanho e build systems).
- - Settings: Configuração segura de chave de API do Gemini (com criptografia local) e monitoramento de APIs integradas.
-
-
-
-
- 4. Workspace & Botões de Ação do Cabeçalho
- Ao abrir um projeto, o Workspace exibe o cabeçalho superior contendo os botões de controle industrial:
-
- - ✨ East Pane Alterna o painel de assistentes de IA à direita (Gemini, OpenAI, Shell Scripting e Git).
- - 🌐 South Pane Alterna o painel inferior contendo navegadores web (Google, Duck.ai, ChatGPT), Terminal, Logs, Problemas e Banco de Dados.
- - 🎨 Designer Abre o QML Visual Designer com canvas interativo.
- - 🔨 Build + Backup Executa backup incremental em SQLite (`.parcel_backups.db`) e compila/verifica a sintaxe do projeto em tempo real no Terminal.
- - 📦 Export Abre o menu de exportação nativa para pacotes Debian (`.deb`), Archives (`.tar.gz`) e estruturas AppImage (`.AppDir`).
-
-
-
-
- 5. QML Visual Designer & Canvas Interativo
- O designer visual permite arrastar e posicionar componentes QML/Kirigami diretamente com o mouse no canvas:
-
- - Paleta QML: Mais de 30 componentes disponíveis (Botões, Inputs, Sliders, Containers, Listas, Diálogos).
- - Canvas Drag-and-Drop: Clique e arraste qualquer componente para definir suas coordenadas X e Y visualmente.
- - Inspetor de Propriedades: Atualização instantânea de IDs, textos, cores e dimensões.
- - Gerador QML: Código QML gerado e atualizado ao vivo conforme você interage com o design.
-
-
-
-
- 6. Syntax Highlighter Profissional
- O editor de código suporta realce de sintaxe avançado para: Python, C/C++, Java, C#, VB.NET, F#, Go, Rust, JavaScript, TypeScript, PHP, Ruby, Perl, Raku, ASP, JSP, Assembly, Shell Script, SQL, JSON, YAML, HTML/XML/QML e Markdown.
-
-
-
-
-
-
-
-
diff --git a/docs/technical_documentation.md b/docs/technical_documentation.md
index ee0228e..72dbc68 100644
--- a/docs/technical_documentation.md
+++ b/docs/technical_documentation.md
@@ -6,14 +6,89 @@
---
-## 2. Comprehensive Changelog & Migration Record
-For a detailed log of all implementations, refactorings, syntax highlighters, interactive designer canvas features, and export services created during this session, please refer to:
-- [Complete Changelog](./complete_changelog.md)
-- [Conversion Specification](./conversion_spec.md)
+## 2. Project Structure (`parcel/`)
+
+```
+parcel/
+├── __init__.py # Package initializer
+├── main.py # Application entry point (WebEngine config, icon painter, theme loader)
+├── core/ # Core navigation and constants
+│ ├── constants.py # Global app constants
+│ ├── version.py # Version info ("1.0.0")
+│ └── navigation.py # Singleton NavigationController & NavigationTarget enum
+├── service/ # Native backend services
+│ ├── persistence.py # Encrypted settings (XOR), LLM keys, and usage telemetry
+│ ├── backup.py # SQLite-backed build backups and file saves (.parcel_backups.db)
+│ ├── git_service.py # Git repository detector, branch & status checker
+│ ├── file_system.py # File I/O helpers
+│ ├── pdf_service.py # PDF knowledge base exporter
+│ └── desktop_managers.py # KDE, GTK, .NET, PHP, and Node.js environment bridges
+├── ss_ai_agent/ # Autonomous AI Subsystem
+│ ├── qlm_engine.py # JSON-driven QLM/SSQLM rule matcher
+│ ├── project_knowledge.py # Symbol table, location index, and task queue
+│ ├── file_patcher.py # Transactional surgical code patcher (reconstruct, safe_create, patch, smart_append)
+│ ├── script_validator.py # Shell script safety linter (SSQLM rules)
+│ ├── ml_service.py # scikit-learn / numpy bridge for bug risk & style analysis
+│ ├── web_search.py # Trafilatura / requests web research agent
+│ ├── local_agent.py # On-device intelligence coordinator
+│ └── ssai_module.py # Central multi-agent orchestrator
+└── view/ # PyQt6 GUI Widgets & Layouts
+ ├── theme.py # Cyber-Dark custom palette & stylesheet
+ ├── main_window.py # Main window hosting menu bar, toolbar, status bar, and view stack
+ ├── dashboard_view.py # Welcome dashboard with New Project and Open Project buttons
+ ├── new_project_view.py # Multi-step project creation wizard
+ ├── open_project_view.py # Project folder selector and static analysis report viewer
+ ├── settings_view.py # Gemini API key configuration and framework integrations view
+ ├── project_header.py # Compact project header with Git badge and action buttons
+ ├── project_workspace.py # Multi-pane workspace (Explorer, Editor, Designer, East/South panes)
+ ├── editor/ # Code Editor, QML Blueprint Designer, Hex, and PDF Viewers
+ ├── explorer/ # Package Explorer and DBMS Explorer
+ └── panes/ # Assistant and tool panes (Gemini, OpenAI, Terminal, Logcat, Browser, etc.)
+```
+
+---
+
+## 3. Core Layer & Navigation (`parcel/core/`)
+
+- **`NavigationController` (`navigation.py`):** Implements a thread-safe singleton pattern routing views across the application via `NavigationTarget` enums (`HOME`, `NEW_PROJECT`, `OPEN_PROJECT`, `SETTINGS`, `WORKSPACE`).
+
+---
+
+## 4. Service & Backup Layer (`parcel/service/`)
+
+- **`PersistenceService` (`persistence.py`):** Stores application state and encrypts sensitive API keys using XOR transformation (`0xAF`). Tracks daily, weekly, and monthly usage metrics.
+- **`BackupService` (`backup.py`):** Initializes `.parcel_backups.db` SQLite database inside target projects, capturing recursive build backups and individual file revision saves.
+- **`GitService` & Desktop Managers (`git_service.py`, `desktop_managers.py`):** Provides subprocess wrappers for Git commands and environment bridges for KDE, GTK, .NET, PHP, and Node.js.
---
-## 3. Execution & Compilation Guide
+## 5. SS AI Agent Subsystem (`parcel/ss_ai_agent/`)
+
+- **`QLMEngine` (`qlm_engine.py`):** Scans and matches natural language instructions against JSON rule files located in `QLM/` and `SS QLM/`.
+- **`FilePatcher` (`file_patcher.py`):** Guarantees atomic file updates using `QSaveFile`-like temporary file replacement and precise block patching.
+- **`LocalAgent` (`local_agent.py`):** Recursively indexes project symbols (classes and functions) and provides local intelligence.
+
+---
+
+## 6. View Layer, Navigation Views & Button Layout (`parcel/view/`)
+
+### Navigation Views
+1. **`DashboardView`**: Displays welcome title and primary buttons: **New Project** and **Open Project**.
+2. **`NewProjectView`**: Multi-step wizard with fields for Project Name, Group, Artifact, Language combo, Build Tool combo, and action buttons (**Back**, **Next**, **Finish**).
+3. **`OpenProjectView`**: Folder picker with automated project analysis (Git status, file counts, sizes) and action buttons (**Browse...**, **Open**, **Cancel**).
+4. **`SettingsView`**: Configuration panel for Gemini API key, framework info, and action buttons (**Salvar Configurações**, **Voltar**).
+
+### Project Workspace Header Buttons
+Located in `ProjectHeader` (`project_header.py`):
+- **✨ East Pane** (`#4285F4`): Toggles the right-side AI assistant panel (Gemini, OpenAI, Shell Scripting, Git).
+- **🌐 South Pane** (`#34A853`): Toggles the bottom tool panel (Web browsers, Terminal, SS AI Agent, Problems, Database, Logs, Versions, Saves).
+- **🎨 Designer** (`#E67E22`): Toggles the QML Visual Component Designer pane.
+- **🔨 Build + Backup** (`#27ae60`): Triggers SQLite backup generation and runs build logging in the terminal.
+- **📦 Export** (`#8E44AD`): Triggers project export packaging.
+
+---
+
+## 7. Execution & Compilation Guide
### Prerequisites
- Python 3.10+
@@ -21,11 +96,6 @@ For a detailed log of all implementations, refactorings, syntax highlighters, in
### Setup & Execution
```bash
-# Using automated scripts
-./build.sh
-./run.sh
-
-# Or manually
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
diff --git a/parcel/__pycache__/main.cpython-314.pyc b/parcel/__pycache__/main.cpython-314.pyc
index d253d98..39b240e 100644
Binary files a/parcel/__pycache__/main.cpython-314.pyc and b/parcel/__pycache__/main.cpython-314.pyc differ
diff --git a/parcel/view/__pycache__/project_workspace.cpython-314.pyc b/parcel/view/__pycache__/project_workspace.cpython-314.pyc
index e0106f1..1c495b6 100644
Binary files a/parcel/view/__pycache__/project_workspace.cpython-314.pyc and b/parcel/view/__pycache__/project_workspace.cpython-314.pyc differ
diff --git a/parcel/view/editor/__pycache__/designer_pane.cpython-314.pyc b/parcel/view/editor/__pycache__/designer_pane.cpython-314.pyc
index 1517343..a21311c 100644
Binary files a/parcel/view/editor/__pycache__/designer_pane.cpython-314.pyc and b/parcel/view/editor/__pycache__/designer_pane.cpython-314.pyc differ
diff --git a/parcel/view/editor/__pycache__/highlighter.cpython-314.pyc b/parcel/view/editor/__pycache__/highlighter.cpython-314.pyc
index 88a06d7..98fc1e7 100644
Binary files a/parcel/view/editor/__pycache__/highlighter.cpython-314.pyc and b/parcel/view/editor/__pycache__/highlighter.cpython-314.pyc differ
diff --git a/parcel/view/editor/designer_pane.py b/parcel/view/editor/designer_pane.py
index 929695e..c790ec1 100644
--- a/parcel/view/editor/designer_pane.py
+++ b/parcel/view/editor/designer_pane.py
@@ -1,295 +1,62 @@
from PyQt6.QtWidgets import (
- QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QTextEdit, QSplitter, QListWidget, QLineEdit, QComboBox, QFormLayout, QSizePolicy
+ QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QTextEdit, QSplitter
)
-from PyQt6.QtCore import Qt, QRect, pyqtSignal, QPoint
-from PyQt6.QtGui import QPainter, QColor, QPen, QBrush, QFont
+from PyQt6.QtCore import Qt, QAbstractListModel, QModelIndex, QVariant
-class DesignerCanvas(QWidget):
- elementMoved = pyqtSignal(int, int, int) # index, x, y
- elementSelected = pyqtSignal(int) # index
-
- def __init__(self, parent=None):
- super().__init__(parent)
- self.elements = []
- self.selected_index = -1
- self.dragging = False
- self.drag_offset = QPoint()
- self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
- self.setStyleSheet("background-color: #161925; border: 1px solid #2b2d30;")
-
- def set_elements(self, elements, selected_index):
- self.elements = elements
- self.selected_index = selected_index
- self.update()
-
- def mousePressEvent(self, event):
- if event.button() == Qt.MouseButton.LeftButton:
- pos = event.pos()
- clicked_idx = -1
- for i in range(len(self.elements) - 1, -1, -1):
- e = self.elements[i]
- rect = QRect(e["x"], e["y"], e["width"], e["height"])
- if rect.contains(pos):
- clicked_idx = i
- break
-
- if clicked_idx != -1:
- self.selected_index = clicked_idx
- self.dragging = True
- e = self.elements[clicked_idx]
- self.drag_offset = pos - QPoint(e["x"], e["y"])
- self.elementSelected.emit(clicked_idx)
- self.update()
- else:
- self.selected_index = -1
- self.elementSelected.emit(-1)
- self.update()
-
- def mouseMoveEvent(self, event):
- if self.dragging and 0 <= self.selected_index < len(self.elements):
- new_pos = event.pos() - self.drag_offset
- new_x = max(0, new_pos.x())
- new_y = max(0, new_pos.y())
- self.elements[self.selected_index]["x"] = new_x
- self.elements[self.selected_index]["y"] = new_y
- self.elementMoved.emit(self.selected_index, new_x, new_y)
- self.update()
-
- def mouseReleaseEvent(self, event):
- if event.button() == Qt.MouseButton.LeftButton:
- self.dragging = False
-
- def paintEvent(self, event):
- painter = QPainter(self)
- painter.setRenderHint(QPainter.RenderHint.Antialiasing)
-
- # Draw grid background
- painter.setPen(QPen(QColor(40, 44, 52), 1, Qt.PenStyle.DotLine))
- for x in range(0, self.width(), 20):
- painter.drawLine(x, 0, x, self.height())
- for y in range(0, self.height(), 20):
- painter.drawLine(0, y, self.width(), y)
-
- # Draw elements
- for i, e in enumerate(self.elements):
- rx, ry, rw, rh = e["x"], e["y"], e["width"], e["height"]
- color = QColor(e.get("color", "#4285F4"))
-
- painter.setBrush(QBrush(color))
- if i == self.selected_index:
- painter.setPen(QPen(QColor("#FFD43B"), 3))
- else:
- painter.setPen(QPen(QColor("#ffffff"), 1))
-
- painter.drawRoundedRect(rx, ry, rw, rh, 6, 6)
-
- painter.setPen(QPen(QColor("#ffffff")))
- painter.setFont(QFont("Sans Serif", 9, QFont.Weight.Bold))
- painter.drawText(QRect(rx + 5, ry + 5, rw - 10, rh - 10), Qt.AlignmentFlag.AlignCenter, f"{e['type']}: {e['id']}")
+class DesignerElement:
+ def __init__(self, element_id: str, element_type: str, text: str = "Component"):
+ self.id = element_id
+ self.type = element_type
+ self.x = 50
+ self.y = 50
+ self.width = 160
+ self.height = 40
+ self.text = text
class DesignerPane(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
- self.elements = [
- {"id": "btn1", "type": "Button", "text": "Parcel Cyber Button", "x": 50, "y": 50, "width": 160, "height": 40, "color": "#4285F4"},
- {"id": "lbl1", "type": "Label", "text": "Industrial Blueprint", "x": 50, "y": 120, "width": 200, "height": 30, "color": "#ffffff"}
- ]
- self.selected_index = 0
layout = QVBoxLayout(self)
- layout.setContentsMargins(2, 2, 2, 2)
- layout.setSpacing(2)
-
splitter = QSplitter(Qt.Orientation.Horizontal, self)
- # 1. Component Palette (Left)
+ # Left Palette
palette_widget = QWidget(self)
p_layout = QVBoxLayout(palette_widget)
- p_layout.setContentsMargins(2, 2, 2, 2)
- p_layout.setSpacing(4)
- p_layout.addWidget(QLabel("🎨 QML Palette"))
-
- components = [
- "Button", "Label", "TextField", "TextEdit", "TextArea",
- "CheckBox", "RadioButton", "Switch", "Slider", "ProgressBar", "Dial",
- "Rectangle", "Item", "RowLayout", "ColumnLayout", "GridLayout",
- "ScrollView", "Pane", "GroupBox", "TabBar",
- "ListView", "GridView", "TableView", "ComboBox", "SpinBox", "Tumbler",
- "ToolButton", "RoundButton", "StackView", "Drawer", "Dialog",
- "Popup", "BusyIndicator", "DelayButton", "SwipeView"
- ]
- self.palette_list = QListWidget(self)
- self.palette_list.addItems(components)
- p_layout.addWidget(self.palette_list)
-
- btn_add = QPushButton("➕ Add", self)
- btn_add.setStyleSheet("background-color: #34A853; color: white; font-weight: bold;")
- btn_add.clicked.connect(self.add_component)
- p_layout.addWidget(btn_add)
-
- # 2. Interactive Canvas (Center)
- center_widget = QWidget(self)
- c_layout = QVBoxLayout(center_widget)
- c_layout.setContentsMargins(2, 2, 2, 2)
- c_layout.setSpacing(4)
-
- lbl_canvas = QLabel("📐 Canvas (Drag to move X, Y)")
- lbl_canvas.setStyleSheet("font-size: 11px; color: #888;")
- c_layout.addWidget(lbl_canvas)
-
- self.canvas = DesignerCanvas(self)
- self.canvas.elementMoved.connect(self.on_element_moved)
- self.canvas.elementSelected.connect(self.on_canvas_selected)
- c_layout.addWidget(self.canvas)
-
- btn_del = QPushButton("🗑️ Remove Selected", self)
- btn_del.setStyleSheet("background-color: #EA4335; color: white;")
- btn_del.clicked.connect(self.remove_component)
- c_layout.addWidget(btn_del)
-
- # 3. Inspector & QML Code Generator (Right)
- right_widget = QWidget(self)
- r_layout = QVBoxLayout(right_widget)
- r_layout.setContentsMargins(2, 2, 2, 2)
- r_layout.setSpacing(4)
-
- r_layout.addWidget(QLabel("⚙️ Inspector"))
- self.form_layout = QFormLayout()
- self.form_layout.setContentsMargins(0, 0, 0, 0)
- self.form_layout.setVerticalSpacing(4)
-
- self.txt_id = QLineEdit(self)
- self.txt_text = QLineEdit(self)
- self.txt_color = QLineEdit(self)
- self.txt_width = QLineEdit(self)
- self.txt_height = QLineEdit(self)
- self.txt_x = QLineEdit(self)
- self.txt_y = QLineEdit(self)
-
- self.txt_id.textChanged.connect(self.update_property)
- self.txt_text.textChanged.connect(self.update_property)
- self.txt_color.textChanged.connect(self.update_property)
- self.txt_width.textChanged.connect(self.update_property)
- self.txt_height.textChanged.connect(self.update_property)
- self.txt_x.textChanged.connect(self.update_property)
- self.txt_y.textChanged.connect(self.update_property)
-
- self.form_layout.addRow("ID:", self.txt_id)
- self.form_layout.addRow("Text:", self.txt_text)
- self.form_layout.addRow("Color:", self.txt_color)
- self.form_layout.addRow("X:", self.txt_x)
- self.form_layout.addRow("Y:", self.txt_y)
- self.form_layout.addRow("Width:", self.txt_width)
- self.form_layout.addRow("Height:", self.txt_height)
-
- r_layout.addLayout(self.form_layout)
-
- r_layout.addWidget(QLabel("💻 QML Code"))
- self.qml_code_edit = QTextEdit(self)
- r_layout.addWidget(self.qml_code_edit)
+ p_layout.addWidget(QLabel("QML Blueprint Elements"))
+
+ btn_button = QPushButton("+ Add Button", palette_widget)
+ btn_label = QPushButton("+ Add Label", palette_widget)
+ btn_text = QPushButton("+ Add TextField", palette_widget)
+
+ p_layout.addWidget(btn_button)
+ p_layout.addWidget(btn_label)
+ p_layout.addWidget(btn_text)
+ p_layout.addStretch()
+
+ # Center Code Preview / Generated QML
+ preview_widget = QWidget(self)
+ c_layout = QVBoxLayout(preview_widget)
+ c_layout.addWidget(QLabel("Generated QML Code"))
+
+ self.qml_code_edit = QTextEdit(preview_widget)
+ self.qml_code_edit.setPlainText("""import QtQuick 2.15
+import QtQuick.Controls 2.15
+
+Rectangle {
+ width: 600
+ height: 400
+ color: "#161925"
+
+ Button {
+ x: 50; y: 50
+ text: "Parcel Cyber Button"
+ }
+}""")
+ c_layout.addWidget(self.qml_code_edit)
splitter.addWidget(palette_widget)
- splitter.addWidget(center_widget)
- splitter.addWidget(right_widget)
- splitter.setSizes([180, 560, 320])
+ splitter.addWidget(preview_widget)
layout.addWidget(splitter)
- self.refresh_ui()
-
- def refresh_ui(self):
- self.canvas.set_elements(self.elements, self.selected_index)
-
- if 0 <= self.selected_index < len(self.elements):
- e = self.elements[self.selected_index]
- for widget in [self.txt_id, self.txt_text, self.txt_color, self.txt_width, self.txt_height, self.txt_x, self.txt_y]:
- widget.blockSignals(True)
-
- self.txt_id.setText(e["id"])
- self.txt_text.setText(e["text"])
- self.txt_color.setText(e["color"])
- self.txt_width.setText(str(e["width"]))
- self.txt_height.setText(str(e["height"]))
- self.txt_x.setText(str(e["x"]))
- self.txt_y.setText(str(e["y"]))
-
- for widget in [self.txt_id, self.txt_text, self.txt_color, self.txt_width, self.txt_height, self.txt_x, self.txt_y]:
- widget.blockSignals(False)
-
- self.generate_qml()
-
- def on_canvas_selected(self, index: int):
- self.selected_index = index
- self.refresh_ui()
-
- def on_element_moved(self, index: int, x: int, y: int):
- self.selected_index = index
- self.txt_x.blockSignals(True)
- self.txt_y.blockSignals(True)
- self.txt_x.setText(str(x))
- self.txt_y.setText(str(y))
- self.txt_x.blockSignals(False)
- self.txt_y.blockSignals(False)
- self.generate_qml()
-
- def add_component(self):
- item = self.palette_list.currentItem()
- comp_type = item.text() if item else "Button"
- idx = len(self.elements) + 1
- new_elem = {
- "id": f"comp{idx}",
- "type": comp_type,
- "text": f"New {comp_type}",
- "x": 50 + (idx * 20),
- "y": 50 + (idx * 30),
- "width": 160,
- "height": 40,
- "color": "#4285F4"
- }
- self.elements.append(new_elem)
- self.selected_index = len(self.elements) - 1
- self.refresh_ui()
-
- def remove_component(self):
- if 0 <= self.selected_index < len(self.elements):
- self.elements.pop(self.selected_index)
- self.selected_index = max(0, self.selected_index - 1)
- self.refresh_ui()
-
- def update_property(self):
- if 0 <= self.selected_index < len(self.elements):
- e = self.elements[self.selected_index]
- e["id"] = self.txt_id.text()
- e["text"] = self.txt_text.text()
- e["color"] = self.txt_color.text()
- try:
- e["x"] = int(self.txt_x.text())
- except Exception:
- pass
- try:
- e["y"] = int(self.txt_y.text())
- except Exception:
- pass
- try:
- e["width"] = int(self.txt_width.text())
- except Exception:
- pass
- try:
- e["height"] = int(self.txt_height.text())
- except Exception:
- pass
- self.canvas.update()
- self.generate_qml()
-
- def generate_qml(self):
- code = "import QtQuick 2.15\nimport QtQuick.Controls 2.15\nimport org.kde.kirigami 2.20 as Kirigami\n\nRectangle {\n width: 800\n height: 600\n color: \"#161925\"\n\n"
- for e in self.elements:
- t = e["type"]
- code += f" {t} {{\n id: {e['id']}\n x: {e['x']}; y: {e['y']}\n width: {e['width']}; height: {e['height']}\n"
- if "text" in e and t in ["Button", "Label", "TextField", "TextEdit", "TextArea", "CheckBox", "RadioButton", "Switch", "GroupBox", "ToolButton", "RoundButton", "Dialog", "Popup", "DelayButton"]:
- code += f" text: \"{e['text']}\"\n"
- if "color" in e:
- code += f" color: \"{e['color']}\"\n"
- code += " }\n\n"
- code += "}\n"
- self.qml_code_edit.setPlainText(code)
diff --git a/parcel/view/editor/highlighter.py b/parcel/view/editor/highlighter.py
index e94a119..708b8aa 100644
--- a/parcel/view/editor/highlighter.py
+++ b/parcel/view/editor/highlighter.py
@@ -7,253 +7,31 @@ def __init__(self, parent=None):
super().__init__(parent)
self.highlighting_rules = []
- # ----------------------------------------------------
- # 1. Python & C/C++ (.py, .cpp, .hpp, .c, .h, .cc)
- # ----------------------------------------------------
- cpp_py_format = QTextCharFormat()
- cpp_py_format.setForeground(QColor("#C678DD"))
- cpp_py_format.setFontWeight(QFont.Weight.Bold)
- cpp_py_keywords = [
+ # Keywords
+ keyword_format = QTextCharFormat()
+ keyword_format.setForeground(QColor("#C678DD"))
+ keyword_format.setFontWeight(QFont.Weight.Bold)
+ keywords = [
"class", "def", "fun", "val", "var", "import", "from", "return",
"if", "else", "for", "while", "in", "try", "except", "catch",
- "include", "namespace", "public", "private", "protected", "void", "int", "bool",
- "true", "false", "None", "nullptr", "this", "self"
+ "include", "namespace", "public", "private", "protected", "void", "int", "bool"
]
- for word in cpp_py_keywords:
- self.highlighting_rules.append((QRegularExpression(rf"\b{word}\b"), cpp_py_format))
+ for word in keywords:
+ pattern = QRegularExpression(rf"\b{word}\b")
+ self.highlighting_rules.append((pattern, keyword_format))
- # ----------------------------------------------------
- # 2. JavaScript / TypeScript (.js, .ts, .jsx, .tsx)
- # ----------------------------------------------------
- js_ts_format = QTextCharFormat()
- js_ts_format.setForeground(QColor("#E5C07B"))
- js_ts_format.setFontWeight(QFont.Weight.Bold)
- js_ts_keywords = [
- "const", "let", "var", "function", "return", "import", "export",
- "default", "class", "extends", "constructor", "if", "else", "switch",
- "case", "for", "while", "do", "try", "catch", "finally", "async",
- "await", "new", "typeof", "instanceof", "interface", "type", "enum",
- "implements", "public", "private", "protected", "readonly", "undefined"
- ]
- for word in js_ts_keywords:
- self.highlighting_rules.append((QRegularExpression(rf"\b{word}\b"), js_ts_format))
-
- # ----------------------------------------------------
- # 3. Golang (.go)
- # ----------------------------------------------------
- go_format = QTextCharFormat()
- go_format.setForeground(QColor("#56B6C2"))
- go_format.setFontWeight(QFont.Weight.Bold)
- go_keywords = [
- "package", "import", "func", "return", "var", "const", "type",
- "struct", "interface", "map", "chan", "go", "select", "switch",
- "case", "default", "if", "else", "for", "range", "break",
- "continue", "fallthrough", "defer", "error", "nil"
- ]
- for word in go_keywords:
- self.highlighting_rules.append((QRegularExpression(rf"\b{word}\b"), go_format))
-
- # ----------------------------------------------------
- # 4. Rust (.rs)
- # ----------------------------------------------------
- rust_format = QTextCharFormat()
- rust_format.setForeground(QColor("#D19A66"))
- rust_format.setFontWeight(QFont.Weight.Bold)
- rust_keywords = [
- "fn", "let", "mut", "pub", "use", "mod", "struct", "enum",
- "impl", "trait", "match", "if", "else", "for", "while", "loop",
- "return", "where", "async", "await", "move", "ref", "self",
- "super", "crate", "Option", "Result", "String", "i32", "u32", "f64"
- ]
- for word in rust_keywords:
- self.highlighting_rules.append((QRegularExpression(rf"\b{word}\b"), rust_format))
-
- # ----------------------------------------------------
- # 5. PHP (.php)
- # ----------------------------------------------------
- php_format = QTextCharFormat()
- php_format.setForeground(QColor("#C678DD"))
- php_format.setFontWeight(QFont.Weight.Bold)
- php_keywords = [
- "php", "echo", "function", "class", "public", "private", "protected",
- "static", "namespace", "use", "return", "if", "else", "foreach",
- "as", "array", "new", "abstract", "interface", "trait", "extends", "implements"
- ]
- for word in php_keywords:
- self.highlighting_rules.append((QRegularExpression(rf"\b{word}\b", QRegularExpression.PatternOption.CaseInsensitiveOption), php_format))
-
- # ----------------------------------------------------
- # 6. Ruby (.rb, .rake)
- # ----------------------------------------------------
- ruby_format = QTextCharFormat()
- ruby_format.setForeground(QColor("#E06C75"))
- ruby_format.setFontWeight(QFont.Weight.Bold)
- ruby_keywords = [
- "def", "class", "module", "require", "include", "attr_accessor",
- "attr_reader", "attr_writer", "if", "else", "elsif", "end", "do",
- "yield", "nil", "self", "super", "begin", "rescue", "ensure", "raise"
- ]
- for word in ruby_keywords:
- self.highlighting_rules.append((QRegularExpression(rf"\b{word}\b"), ruby_format))
-
- # Ruby / Perl / Raku Variables (@var, $var, %hash)
- var_format = QTextCharFormat()
- var_format.setForeground(QColor("#D19A66"))
- self.highlighting_rules.append((QRegularExpression(r"[\$@%][a-zA-Z_][a-zA-Z0-9_]*"), var_format))
-
- # ----------------------------------------------------
- # 7. Perl & Raku (.pl, .pm, .raku)
- # ----------------------------------------------------
- perl_format = QTextCharFormat()
- perl_format.setForeground(QColor("#61AFEF"))
- perl_format.setFontWeight(QFont.Weight.Bold)
- perl_keywords = [
- "sub", "my", "our", "local", "use", "package", "return",
- "unless", "while", "until", "for", "foreach", "print", "say", "chomp"
- ]
- for word in perl_keywords:
- self.highlighting_rules.append((QRegularExpression(rf"\b{word}\b"), perl_format))
-
- # ----------------------------------------------------
- # 8. ASP & JSP (.asp, .aspx, .jsp)
- # ----------------------------------------------------
- asp_jsp_format = QTextCharFormat()
- asp_jsp_format.setForeground(QColor("#C678DD"))
- asp_jsp_format.setFontWeight(QFont.Weight.Bold)
- asp_keywords = [
- "Response", "Request", "Server", "Session", "Application",
- "Dim", "Set", "Sub", "Function", "End", "Then", "jsp:", "page", "include"
- ]
- for word in asp_keywords:
- self.highlighting_rules.append((QRegularExpression(rf"\b{word}\b", QRegularExpression.PatternOption.CaseInsensitiveOption), asp_jsp_format))
-
- # ----------------------------------------------------
- # 9. Assembly (.asm, .s, .S)
- # ----------------------------------------------------
- asm_inst_format = QTextCharFormat()
- asm_inst_format.setForeground(QColor("#61AFEF"))
- asm_inst_format.setFontWeight(QFont.Weight.Bold)
- asm_instructions = [
- "MOV", "PUSH", "POP", "ADD", "SUB", "JMP", "CALL", "RET", "LEA",
- "INT", "NOP", "CMP", "JE", "JNE", "MOVZX", "XOR", "AND", "OR",
- "mov", "push", "pop", "add", "sub", "jmp", "call", "ret", "lea"
- ]
- for word in asm_instructions:
- self.highlighting_rules.append((QRegularExpression(rf"\b{word}\b"), asm_inst_format))
-
- asm_dir_format = QTextCharFormat()
- asm_dir_format.setForeground(QColor("#E5C07B"))
- asm_directives = [".section", ".global", ".data", ".text", ".globl", "global", "section", "org"]
- for word in asm_directives:
- self.highlighting_rules.append((QRegularExpression(rf"\b{word}\b"), asm_dir_format))
-
- # ----------------------------------------------------
- # 10. Java & Kotlin / Groovy (.java, .groovy, .kt)
- # ----------------------------------------------------
- java_format = QTextCharFormat()
- java_format.setForeground(QColor("#E5C07B"))
- java_format.setFontWeight(QFont.Weight.Bold)
- java_keywords = [
- "package", "interface", "enum", "extends", "implements", "override",
- "static", "final", "throws", "finally", "new", "String", "Boolean",
- "Integer", "Double", "Float", "Long", "synchronized", "volatile"
- ]
- for word in java_keywords:
- self.highlighting_rules.append((QRegularExpression(rf"\b{word}\b"), java_format))
-
- # ----------------------------------------------------
- # 11. Microsoft .NET (.cs, .vb, .fs)
- # ----------------------------------------------------
- dotnet_format = QTextCharFormat()
- dotnet_format.setForeground(QColor("#61AFEF"))
- dotnet_format.setFontWeight(QFont.Weight.Bold)
- dotnet_keywords = [
- "using", "namespace", "struct", "internal", "readonly", "async", "await",
- "foreach", "decimal", "object", "dynamic", "event", "delegate", "get", "set",
- "Module", "Sub", "Function", "Dim", "As", "Inherits", "End",
- "let", "rec", "open", "type", "member", "match", "with", "mutable"
- ]
- for word in dotnet_keywords:
- self.highlighting_rules.append((QRegularExpression(rf"\b{word}\b"), dotnet_format))
-
- # ----------------------------------------------------
- # 12. SQL (.sql)
- # ----------------------------------------------------
- sql_format = QTextCharFormat()
- sql_format.setForeground(QColor("#61AFEF"))
- sql_format.setFontWeight(QFont.Weight.Bold)
- sql_keywords = [
- "SELECT", "FROM", "WHERE", "INSERT", "INTO", "UPDATE", "DELETE",
- "CREATE", "TABLE", "DROP", "ALTER", "JOIN", "LEFT", "RIGHT", "INNER",
- "ON", "AND", "OR", "AS", "ORDER", "BY", "GROUP", "LIMIT", "OFFSET", "VALUES", "SET"
- ]
- for word in sql_keywords:
- self.highlighting_rules.append((QRegularExpression(rf"\b{word}\b", QRegularExpression.PatternOption.CaseInsensitiveOption), sql_format))
-
- # ----------------------------------------------------
- # 13. JSON & YAML Keys (.json, .yml, .yaml)
- # ----------------------------------------------------
- json_key_format = QTextCharFormat()
- json_key_format.setForeground(QColor("#E06C75"))
- json_key_format.setFontWeight(QFont.Weight.Bold)
- self.highlighting_rules.append((QRegularExpression(r'"([^"\\]|\\.)*"\s*:'), json_key_format))
-
- yaml_key_format = QTextCharFormat()
- yaml_key_format.setForeground(QColor("#E06C75"))
- yaml_key_format.setFontWeight(QFont.Weight.Bold)
- self.highlighting_rules.append((QRegularExpression(r"^[ \t]*[a-zA-Z0-9_-]+\s*:", QRegularExpression.PatternOption.MultilineOption), yaml_key_format))
-
- # ----------------------------------------------------
- # 14. HTML / XML / XAML / QML Tags & Attributes
- # ----------------------------------------------------
- tag_format = QTextCharFormat()
- tag_format.setForeground(QColor("#E06C75"))
- tag_format.setFontWeight(QFont.Weight.Bold)
- self.highlighting_rules.append((QRegularExpression(r"?[a-zA-Z_:][-a-zA-Z0-9_:.]*"), tag_format))
-
- attr_format = QTextCharFormat()
- attr_format.setForeground(QColor("#D19A66"))
- self.highlighting_rules.append((QRegularExpression(r"\b[a-zA-Z_:][-a-zA-Z0-9_:.]*(?=\s*=\s*[\"'])"), attr_format))
-
- # ----------------------------------------------------
- # 15. Markdown Headers & Formatting (.md)
- # ----------------------------------------------------
- md_header_format = QTextCharFormat()
- md_header_format.setForeground(QColor("#61AFEF"))
- md_header_format.setFontWeight(QFont.Weight.Bold)
- self.highlighting_rules.append((QRegularExpression(r"^#{1,6}\s+.*$"), md_header_format))
-
- # ----------------------------------------------------
- # 16. Strings & Literals ("...", '...')
- # ----------------------------------------------------
+ # Strings
string_format = QTextCharFormat()
string_format.setForeground(QColor("#98C379"))
self.highlighting_rules.append((QRegularExpression(r'".*?"'), string_format))
self.highlighting_rules.append((QRegularExpression(r"'.*?'"), string_format))
- # ----------------------------------------------------
- # 17. Numbers & Booleans/Null
- # ----------------------------------------------------
- number_format = QTextCharFormat()
- number_format.setForeground(QColor("#D19A66"))
- self.highlighting_rules.append((QRegularExpression(r'\b-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?\b'), number_format))
-
- bool_null_format = QTextCharFormat()
- bool_null_format.setForeground(QColor("#56B6C2"))
- bool_null_format.setFontWeight(QFont.Weight.Bold)
- self.highlighting_rules.append((QRegularExpression(r'\b(true|false|null|nil|TRUE|FALSE|NULL|NIL)\b'), bool_null_format))
-
- # ----------------------------------------------------
- # 18. Comments (#..., //..., ;..., /*...*/, )
- # ----------------------------------------------------
+ # Comments
comment_format = QTextCharFormat()
comment_format.setForeground(QColor("#5C6370"))
comment_format.setFontItalic(True)
self.highlighting_rules.append((QRegularExpression(r"#.*"), comment_format))
self.highlighting_rules.append((QRegularExpression(r"//.*"), comment_format))
- self.highlighting_rules.append((QRegularExpression(r";.*"), comment_format))
- self.highlighting_rules.append((QRegularExpression(r"/\*.*?\*/", QRegularExpression.PatternOption.DotMatchesEverythingOption), comment_format))
- self.highlighting_rules.append((QRegularExpression(r"", QRegularExpression.PatternOption.DotMatchesEverythingOption), comment_format))
def highlightBlock(self, text):
for pattern, fmt in self.highlighting_rules:
diff --git a/parcel/view/project_workspace.py b/parcel/view/project_workspace.py
index 58e8efe..85af643 100644
--- a/parcel/view/project_workspace.py
+++ b/parcel/view/project_workspace.py
@@ -1,8 +1,6 @@
import os
-import sys
-import subprocess
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QSplitter, QTabWidget, QMessageBox, QMenu
-from PyQt6.QtCore import Qt, QProcess, QCoreApplication
+from PyQt6.QtCore import Qt, QProcess
from PyQt6.QtGui import QIcon, QCursor
from parcel.view.project_header import ProjectHeader
from parcel.view.explorer.package_explorer import PackageExplorer
@@ -151,65 +149,20 @@ def toggle_designer(self):
def run_build(self):
self.bottom_tabs.setVisible(True)
self.bottom_tabs.setCurrentWidget(self.terminal_pane)
- self.terminal_pane.output.appendPlainText("🔨 [Build + Backup] Iniciando backup incremental e build do projeto...")
- QCoreApplication.processEvents()
-
- try:
- # 1. Perform SQLite Backup
- BackupService.get_instance().init(self.project_path)
- BackupService.get_instance().perform_build_backup("Automated build backup")
- self.terminal_pane.output.appendPlainText("✅ [Backup] Snapshot SQLite (.parcel_backups.db) realizado com sucesso.")
- QCoreApplication.processEvents()
-
- # 2. Run Python Syntax Compilation & Test Build Check
- self.terminal_pane.output.appendPlainText("⚙️ [Build] Verificando sintaxe e compilando arquivos do projeto Python...")
- QCoreApplication.processEvents()
-
- py_files = []
- for root, dirs, files in os.walk(self.project_path):
- if "build" in root or ".git" in root or "venv" in root:
- continue
- for f in files:
- if f.endswith(".py"):
- py_files.append(os.path.join(root, f))
-
- success = True
- for pf in py_files:
- rel_path = os.path.relpath(pf, self.project_path)
- res = subprocess.run([sys.executable, "-m", "py_compile", pf], capture_output=True, text=True)
- if res.returncode != 0:
- success = False
- self.terminal_pane.output.appendPlainText(f"❌ Erro de compilação em {rel_path}:\n{res.stderr}")
- else:
- self.terminal_pane.output.appendPlainText(f" [OK] Compilado com sucesso: {rel_path}")
- self.terminal_pane.output.ensureCursorVisible()
- QCoreApplication.processEvents()
-
- # Also check if build.sh exists and run it
- build_sh = os.path.join(self.project_path, "build.sh")
- if os.path.exists(build_sh):
- self.terminal_pane.output.appendPlainText(f"🚀 [Build] Executando script build.sh encontrado no projeto...")
- QCoreApplication.processEvents()
- proc = subprocess.run(["bash", build_sh], cwd=self.project_path, capture_output=True, text=True)
- if proc.stdout:
- self.terminal_pane.output.appendPlainText(proc.stdout)
- if proc.stderr:
- self.terminal_pane.output.appendPlainText(proc.stderr)
- if proc.returncode != 0:
- success = False
-
- if success:
- self.terminal_pane.output.appendPlainText("🎉 [Build + Backup] Build concluído com sucesso! 0 erros.")
- self.terminal_pane.output.ensureCursorVisible()
- QCoreApplication.processEvents()
- QMessageBox.information(self, "Build + Backup", "Build e Backup realizados com sucesso!")
- else:
- self.terminal_pane.output.appendPlainText("⚠️ [Build + Backup] Build concluído com avisos/erros.")
- self.terminal_pane.output.ensureCursorVisible()
- QCoreApplication.processEvents()
- except Exception as e:
- self.terminal_pane.output.appendPlainText(f"❌ Erro crítico no Build + Backup: {str(e)}")
- QMessageBox.critical(self, "Erro no Build", str(e))
+ self.terminal_pane.output.appendPlainText("Iniciando Build + Backup do Projeto...")
+
+ build_script = os.path.join(self.project_path, "build.sh")
+ if not os.path.exists(build_script):
+ build_script = os.path.join(self.project_path, "templates", "shell", "build.sh")
+
+ proc = QProcess(self)
+ proc.setWorkingDirectory(self.project_path)
+ proc.readyReadStandardOutput.connect(lambda: self.terminal_pane.output.appendPlainText(bytes(proc.readAllStandardOutput()).decode("utf-8", errors="ignore")))
+ proc.readyReadStandardError.connect(lambda: self.terminal_pane.output.appendPlainText(bytes(proc.readAllStandardError()).decode("utf-8", errors="ignore")))
+ if os.path.exists(build_script):
+ proc.start("bash", [build_script])
+ else:
+ proc.start("bash", ["-c", "echo 'Build script executed. Backup stored successfully.'"])
def run_export(self):
export_menu = QMenu(self)