diff --git a/QmlComponents b/QmlComponents deleted file mode 120000 index fafaa673a..000000000 --- a/QmlComponents +++ /dev/null @@ -1 +0,0 @@ -/Users/rmac/Documents/GitHub/metabuilder/qml \ No newline at end of file diff --git a/frontends/qt6/CMakeLists.txt b/frontends/qt6/CMakeLists.txt index 80ffc4a84..f6923a573 100644 --- a/frontends/qt6/CMakeLists.txt +++ b/frontends/qt6/CMakeLists.txt @@ -1,5 +1,5 @@ # AUTO-GENERATED by generate_cmake.py — do not edit manually -# Generated from cmake_config.json | 53 QML files, 6 C++ sources, 22 SVGs, 1 audio assets +# Generated from cmake_config.json | 192 QML files, 6 C++ sources, 22 SVGs, 1 audio assets # # Discovered packages: # analytics v1.0.0 - Analytics Studio @@ -58,36 +58,175 @@ qt_add_qml_module(dbal-qml URI DBALObservatory VERSION 1.0 QML_FILES - ../../qml/qt6/AdminCrud.js - ../../qml/qt6/AdminView.qml - ../../qml/qt6/App.qml - ../../qml/qt6/CommentsView.qml - ../../qml/qt6/ComponentHierarchyEditor.qml - ../../qml/qt6/CssClassManager.qml - ../../qml/qt6/DashboardView.qml - ../../qml/qt6/DatabaseManager.qml - ../../qml/qt6/DropdownConfigManager.qml - ../../qml/qt6/FrontPage.qml - ../../qml/qt6/GodPanel.qml - ../../qml/qt6/LoginView.qml - ../../qml/qt6/LuaEditor.qml - ../../qml/qt6/MaterialLanding.qml - ../../qml/qt6/MediaServicePanel.qml - ../../qml/qt6/ModPlayerPanel.qml - ../../qml/qt6/ModeratorView.qml - ../../qml/qt6/NotificationsPanel.qml - ../../qml/qt6/PackageManager.qml - ../../qml/qt6/PackageViewLoader.qml - ../../qml/qt6/PageRoutesManager.qml - ../../qml/qt6/ProfileView.qml - ../../qml/qt6/SMTPConfigEditor.qml - ../../qml/qt6/SchemaEditor.qml - ../../qml/qt6/SettingsView.qml - ../../qml/qt6/Storybook.qml - ../../qml/qt6/SuperGodPanel.qml - ../../qml/qt6/ThemeEditor.qml - ../../qml/qt6/UserManagement.qml - ../../qml/qt6/WorkflowEditor.qml + AdminCrud.js + AdminView.qml + App.qml + CommentsView.qml + ComponentHierarchyEditor.qml + CssClassManager.qml + DashboardView.qml + DatabaseManager.qml + DropdownConfigManager.qml + FrontPage.qml + GodPanel.qml + LoginView.qml + LuaEditor.qml + MaterialLanding.qml + MediaServicePanel.qml + ModPlayerPanel.qml + ModeratorView.qml + NotificationsPanel.qml + PackageManager.qml + PackageViewLoader.qml + PageRoutesManager.qml + ProfileView.qml + SMTPConfigEditor.qml + SchemaEditor.qml + SettingsView.qml + Storybook.qml + SuperGodPanel.qml + ThemeEditor.qml + UserManagement.qml + WorkflowEditor.qml + qmllib/Material/MaterialAccordion.qml + qmllib/Material/MaterialAlert.qml + qmllib/Material/MaterialAppBar.qml + qmllib/Material/MaterialAvatar.qml + qmllib/Material/MaterialBadge.qml + qmllib/Material/MaterialBox.qml + qmllib/Material/MaterialButton.qml + qmllib/Material/MaterialCard.qml + qmllib/Material/MaterialCheckbox.qml + qmllib/Material/MaterialChip.qml + qmllib/Material/MaterialCircularProgress.qml + qmllib/Material/MaterialCollapse.qml + qmllib/Material/MaterialContainer.qml + qmllib/Material/MaterialDialog.qml + qmllib/Material/MaterialDivider.qml + qmllib/Material/MaterialDividerProps.qml + qmllib/Material/MaterialGrid.qml + qmllib/Material/MaterialIconButton.qml + qmllib/Material/MaterialLinearProgress.qml + qmllib/Material/MaterialLink.qml + qmllib/Material/MaterialMenu.qml + qmllib/Material/MaterialMenuItem.qml + qmllib/Material/MaterialMenuProps.qml + qmllib/Material/MaterialPalette.qml + qmllib/Material/MaterialPaper.qml + qmllib/Material/MaterialPopover.qml + qmllib/Material/MaterialPopoverProps.qml + qmllib/Material/MaterialSkeleton.qml + qmllib/Material/MaterialSnackbar.qml + qmllib/Material/MaterialSurface.qml + qmllib/Material/MaterialSwitch.qml + qmllib/Material/MaterialTextField.qml + qmllib/Material/MaterialToolbar.qml + qmllib/Material/MaterialTypography.qml + qmllib/MetaBuilder/CActivityList.qml + qmllib/MetaBuilder/CAdapterPatternSelector.qml + qmllib/MetaBuilder/CAddRouteDialog.qml + qmllib/MetaBuilder/CAdminStatsBar.qml + qmllib/MetaBuilder/CBackendDetailPanel.qml + qmllib/MetaBuilder/CBackendListSidebar.qml + qmllib/MetaBuilder/CCanvasGrid.qml + qmllib/MetaBuilder/CCanvasZoomOverlay.qml + qmllib/MetaBuilder/CCommentCard.qml + qmllib/MetaBuilder/CCommentInput.qml + qmllib/MetaBuilder/CComponentPropertiesPanel.qml + qmllib/MetaBuilder/CComponentTreeRow.qml + qmllib/MetaBuilder/CComponentTypeLegend.qml + qmllib/MetaBuilder/CConfigStatCard.qml + qmllib/MetaBuilder/CConnectionLayer.qml + qmllib/MetaBuilder/CConnectionTest.qml + qmllib/MetaBuilder/CDataTable.qml + qmllib/MetaBuilder/CDatabaseStatsRow.qml + qmllib/MetaBuilder/CDeleteConfirmDialog.qml + qmllib/MetaBuilder/CDropdownMenu.qml + qmllib/MetaBuilder/CEntityForm.qml + qmllib/MetaBuilder/CEntitySidebar.qml + qmllib/MetaBuilder/CGodPanelHeader.qml + qmllib/MetaBuilder/CGodUserCard.qml + qmllib/MetaBuilder/CHeroSection.qml + qmllib/MetaBuilder/CLanguageSelector.qml + qmllib/MetaBuilder/CLevelCard.qml + qmllib/MetaBuilder/CLevelReferenceCard.qml + qmllib/MetaBuilder/CLoginForm.qml + qmllib/MetaBuilder/CModActionCard.qml + qmllib/MetaBuilder/CModStatsRow.qml + qmllib/MetaBuilder/CNavBar.qml + qmllib/MetaBuilder/CNodePalette.qml + qmllib/MetaBuilder/CNodePropertiesPanel.qml + qmllib/MetaBuilder/CNotificationBell.qml + qmllib/MetaBuilder/CNotificationEmptyState.qml + qmllib/MetaBuilder/CNotificationItem.qml + qmllib/MetaBuilder/CNotificationToggles.qml + qmllib/MetaBuilder/CProfileForm.qml + qmllib/MetaBuilder/CProfileHeader.qml + qmllib/MetaBuilder/CQuickActions.qml + qmllib/MetaBuilder/CQuickLoginCard.qml + qmllib/MetaBuilder/CReportCard.qml + qmllib/MetaBuilder/CRouteEditPanel.qml + qmllib/MetaBuilder/CRouteTableHeader.qml + qmllib/MetaBuilder/CRouteTableRow.qml + qmllib/MetaBuilder/CServiceStatus.qml + qmllib/MetaBuilder/CSettingsSection.qml + qmllib/MetaBuilder/CSidebar.qml + qmllib/MetaBuilder/CSmtpSenderForm.qml + qmllib/MetaBuilder/CSmtpServerForm.qml + qmllib/MetaBuilder/CSmtpTemplateEditor.qml + qmllib/MetaBuilder/CSmtpTemplateList.qml + qmllib/MetaBuilder/CSmtpTestEmailForm.qml + qmllib/MetaBuilder/CStatCard.qml + qmllib/MetaBuilder/CStatsStrip.qml + qmllib/MetaBuilder/CSystemMetricCard.qml + qmllib/MetaBuilder/CTableHeader.qml + qmllib/MetaBuilder/CTablePagination.qml + qmllib/MetaBuilder/CTechCard.qml + qmllib/MetaBuilder/CTenantCard.qml + qmllib/MetaBuilder/CThemePicker.qml + qmllib/MetaBuilder/CTransferCard.qml + qmllib/MetaBuilder/CUserMenu.qml + qmllib/MetaBuilder/CWelcomeCard.qml + qmllib/MetaBuilder/CWorkflowCanvas.qml + qmllib/MetaBuilder/CWorkflowNodeDelegate.qml + qmllib/MetaBuilder/CWorkflowSidebar.qml + qmllib/MetaBuilder/CWorkflowTestPanel.qml + qmllib/MetaBuilder/CWorkflowToolbar.qml + qmllib/MetaBuilder/ContactForm.qml + qmllib/MetaBuilder/CssClassPreview.qml + qmllib/MetaBuilder/CssClassSidebar.qml + qmllib/MetaBuilder/CssPropertyEditor.qml + qmllib/MetaBuilder/DropdownGeneralForm.qml + qmllib/MetaBuilder/DropdownOptionsEditor.qml + qmllib/MetaBuilder/DropdownPreview.qml + qmllib/MetaBuilder/DropdownSidebar.qml + qmllib/MetaBuilder/FeatureCard.qml + qmllib/MetaBuilder/HeroSection.qml + qmllib/MetaBuilder/LuaCodeEditor.qml + qmllib/MetaBuilder/LuaOutputPanel.qml + qmllib/MetaBuilder/LuaPropertiesPanel.qml + qmllib/MetaBuilder/LuaScriptSidebar.qml + qmllib/MetaBuilder/MediaJobForm.qml + qmllib/MetaBuilder/MediaJobTable.qml + qmllib/MetaBuilder/MediaPluginsTab.qml + qmllib/MetaBuilder/MediaRadioTab.qml + qmllib/MetaBuilder/MediaTvTab.qml + qmllib/MetaBuilder/NavBar.qml + qmllib/MetaBuilder/SchemaFieldEditor.qml + qmllib/MetaBuilder/SchemaFieldsTable.qml + qmllib/MetaBuilder/SchemaSidebar.qml + qmllib/MetaBuilder/StatusCard.qml + qmllib/MetaBuilder/ThemeColorTokens.qml + qmllib/MetaBuilder/ThemeLivePreview.qml + qmllib/MetaBuilder/ThemePresetGrid.qml + qmllib/MetaBuilder/ThemeSpacingRadius.qml + qmllib/MetaBuilder/ThemeTypography.qml + qmllib/MetaBuilder/UserFormDialog.qml + qmllib/MetaBuilder/UserSearchFilter.qml + qmllib/MetaBuilder/UserStatsBar.qml + qmllib/MetaBuilder/UserTable.qml + qmllib/MetaBuilder/WorkflowNode.qml + qmllib/dbal/DBALProvider.qml packages/analytics/PackageView.qml packages/blog/PackageView.qml packages/breakout/PackageView.qml @@ -130,6 +269,9 @@ qt_add_qml_module(dbal-qml config/supergod-tenants.json config/supergod-transfers.json config/workflow-mock-data.json + qmllib/Material/qmldir + qmllib/MetaBuilder/qmldir + qmllib/dbal/qmldir ) # SVG assets diff --git a/frontends/qt6/generate_cmake.py b/frontends/qt6/generate_cmake.py index 074df9667..b326e7c13 100755 --- a/frontends/qt6/generate_cmake.py +++ b/frontends/qt6/generate_cmake.py @@ -29,43 +29,64 @@ def load_config(config_path: str) -> dict: return json.load(f) -def find_root_qml_files(root_dir: Path) -> list[str]: - """Find all *.qml and *.js files in the project root and ../../qml/qt6/ directories.""" - files = sorted( - list(root_dir.glob("*.qml")) + list(root_dir.glob("*.js")) - ) - result = [f.name for f in files] +def find_root_qml_files(root_dir: Path) -> list[tuple[str, str]]: + """Find all *.qml and *.js files in the project root and extracted qml/qt6/ directory. - # Also scan the extracted QML directory (../../qml/qt6/ relative to root_dir) + Returns list of (source_path, qrc_alias) tuples. Files in root_dir use just their + filename; files in ../../qml/qt6/ use relative paths with a QT_RESOURCE_ALIAS. + """ + result = [] + # Local root files + for fn in sorted(os.listdir(str(root_dir))): + if fn.endswith((".qml", ".js")): + result.append((fn, None)) + + # Extracted files in ../../qml/qt6/ extracted_dir = root_dir.parent.parent / "qml" / "qt6" if extracted_dir.exists(): - ext_files = sorted( - list(extracted_dir.glob("*.qml")) + list(extracted_dir.glob("*.js")) - ) - for f in ext_files: - rel = os.path.relpath(f, root_dir) - if rel not in result: - result.append(rel) - return sorted(result) + local_names = {t[0] for t in result} + for fn in sorted(os.listdir(str(extracted_dir))): + if fn.endswith((".qml", ".js")) and fn not in local_names: + rel_path = os.path.relpath(extracted_dir / fn, root_dir) + result.append((rel_path, fn)) + return result def find_qmllib_files(root_dir: Path) -> dict[str, list[str]]: """Find all *.qml and *.js files and qmldir files in qmllib/ subdirectories. - Returns a dict mapping relative paths (e.g. 'qmllib/dbal/DBALProvider.qml') - grouped by subdirectory. + Follows symlinks so that extracted component directories (e.g., qmllib/MetaBuilder + symlinked to ../../qml/MetaBuilder) are included with qmllib-relative paths. + + Returns a dict with 'qml' (list of QML/JS paths) and 'resources' (qmldir paths). """ - qmllib_dir = root_dir / "qmllib" result = {"qml": [], "resources": []} - if not qmllib_dir.exists(): - return result - qml_files = sorted( - list(qmllib_dir.rglob("*.qml")) + list(qmllib_dir.rglob("*.js")) - ) - for qml_file in qml_files: - result["qml"].append(str(qml_file.relative_to(root_dir))) - for qmldir_file in sorted(qmllib_dir.rglob("qmldir")): - result["resources"].append(str(qmldir_file.relative_to(root_dir))) + + # Search both local qmllib/ and extracted ../../qml/{MetaBuilder,Material,dbal} + search_dirs = [] + qmllib_dir = root_dir / "qmllib" + if qmllib_dir.exists(): + search_dirs.append((qmllib_dir, "qmllib")) + + extracted_qml = root_dir.parent.parent / "qml" + if extracted_qml.exists(): + for subdir in ["MetaBuilder", "Material", "dbal"]: + candidate = extracted_qml / subdir + if candidate.exists(): + search_dirs.append((candidate, f"qmllib/{subdir}")) + + for search_dir, prefix in search_dirs: + for dirpath, _dirnames, filenames in os.walk(str(search_dir), followlinks=True): + for fn in filenames: + full = os.path.join(dirpath, fn) + # Compute path relative to search_dir, then prepend prefix + rel_to_search = os.path.relpath(full, str(search_dir)) + aliased = f"{prefix}/{rel_to_search}" if prefix == f"qmllib/{search_dir.name}" else os.path.relpath(full, str(root_dir)) + real_path = os.path.relpath(full, str(root_dir)) + if fn.endswith((".qml", ".js")): + result["qml"].append((real_path, f"{prefix}/{rel_to_search}")) + elif fn == "qmldir": + result["resources"].append((real_path, f"{prefix}/{rel_to_search}")) return result diff --git a/frontends/qt6/main.cpp b/frontends/qt6/main.cpp index 1585f3533..0d5d7a5b7 100644 --- a/frontends/qt6/main.cpp +++ b/frontends/qt6/main.cpp @@ -14,25 +14,16 @@ int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; - // Add shared QML component library path - // Resolves: import QmlComponents 1.0 - const auto appDir = QCoreApplication::applicationDirPath(); - // Qt6 resolves "import QmlComponents" by looking for a QmlComponents/ dir - // inside each import path. We symlink or reference the parent of qml/. - const QStringList qmlParentPaths = { - appDir + "/../../", - appDir + "/../../../", - appDir + "/../../../../", - QDir::cleanPath(QStringLiteral(SRCDIR) + "/../..") - }; - for (const auto &path : qmlParentPaths) { - const QString candidate = QDir(path).absolutePath(); - // Check if QmlComponents symlink or qml/ dir with qmldir exists - if (QDir(candidate + "/QmlComponents").exists() - || QDir(candidate + "/qml").exists()) { - engine.addImportPath(candidate); - break; - } + // Add shared QML component library import paths + // No symlinks — directly reference the qml/ directory tree + const QString projectRoot = QDir::cleanPath(QStringLiteral(SRCDIR) + QStringLiteral("/../..")); + const QString qmlDir = projectRoot + QStringLiteral("/qml"); + + // Add qml/ parent so Qt finds "import QmlComponents 1.0" at qml/components/ + // and "import MetaBuilder 1.0" at qml/MetaBuilder/ + if (QDir(qmlDir).exists()) { + engine.addImportPath(qmlDir); + engine.addImportPath(projectRoot); } PackageRegistry registry; diff --git a/qml/MetaBuilder/CSmtpSenderForm.qml b/qml/MetaBuilder/CSmtpSenderForm.qml index 925d3f389..d01f9937d 100644 --- a/qml/MetaBuilder/CSmtpSenderForm.qml +++ b/qml/MetaBuilder/CSmtpSenderForm.qml @@ -12,8 +12,8 @@ CCard { property string fromName: "" property string fromEmail: "" - signal fromNameChanged(string value) - signal fromEmailChanged(string value) + signal fromNameEdited(string value) + signal fromEmailEdited(string value) ColumnLayout { anchors.fill: parent @@ -28,7 +28,7 @@ CCard { label: "From Name" placeholderText: "MetaBuilder" text: root.fromName - onTextChanged: root.fromNameChanged(text) + onTextChanged: root.fromNameEdited(text) } CTextField { @@ -36,7 +36,7 @@ CCard { label: "From Email" placeholderText: "noreply@example.com" text: root.fromEmail - onTextChanged: root.fromEmailChanged(text) + onTextChanged: root.fromEmailEdited(text) } CDivider { Layout.fillWidth: true } diff --git a/qml/MetaBuilder/CSmtpTestEmailForm.qml b/qml/MetaBuilder/CSmtpTestEmailForm.qml index d268ffc78..cf5d05370 100644 --- a/qml/MetaBuilder/CSmtpTestEmailForm.qml +++ b/qml/MetaBuilder/CSmtpTestEmailForm.qml @@ -12,9 +12,9 @@ CCard { property string body: "This is a test email from MetaBuilder." property bool sending: false - signal recipientChanged(string value) - signal subjectChanged(string value) - signal bodyChanged(string value) + signal recipientEdited(string value) + signal subjectEdited(string value) + signal bodyEdited(string value) signal sendRequested() ColumnLayout { @@ -34,7 +34,7 @@ CCard { label: "Recipient" placeholderText: "test@example.com" text: root.recipient - onTextChanged: root.recipientChanged(text) + onTextChanged: root.recipientEdited(text) } CTextField { @@ -42,7 +42,7 @@ CCard { label: "Subject" placeholderText: "Test subject" text: root.subject - onTextChanged: root.subjectChanged(text) + onTextChanged: root.subjectEdited(text) } } @@ -70,7 +70,7 @@ CCard { color: Theme.text font.pixelSize: 13 background: null - onTextChanged: root.bodyChanged(text) + onTextChanged: root.bodyEdited(text) } } } diff --git a/qml/MetaBuilder/ThemeSpacingRadius.qml b/qml/MetaBuilder/ThemeSpacingRadius.qml index a7daf2c5a..bbaa43c0d 100644 --- a/qml/MetaBuilder/ThemeSpacingRadius.qml +++ b/qml/MetaBuilder/ThemeSpacingRadius.qml @@ -13,10 +13,10 @@ ColumnLayout { property int radiusMedium: 8 property int radiusLarge: 16 - signal baseSpacingChanged(int value) - signal radiusSmallChanged(int value) - signal radiusMediumChanged(int value) - signal radiusLargeChanged(int value) + signal baseSpacingEdited(int value) + signal radiusSmallEdited(int value) + signal radiusMediumEdited(int value) + signal radiusLargeEdited(int value) // Spacing section CCard { diff --git a/qml/qt6/SMTPConfigEditor.qml b/qml/qt6/SMTPConfigEditor.qml index d36345003..91368d95e 100644 --- a/qml/qt6/SMTPConfigEditor.qml +++ b/qml/qt6/SMTPConfigEditor.qml @@ -143,16 +143,16 @@ Rectangle { CSmtpSenderForm { fromName: smtpEditor.fromName; fromEmail: smtpEditor.fromEmail - onFromNameChanged: function(v) { smtpEditor.fromName = v; markDirty() } - onFromEmailChanged: function(v) { smtpEditor.fromEmail = v; markDirty() } + onFromNameEdited: function(v) { smtpEditor.fromName = v; markDirty() } + onFromEmailEdited: function(v) { smtpEditor.fromEmail = v; markDirty() } } } CSmtpTestEmailForm { recipient: testRecipient; subject: testSubject; body: testBody; sending: sendingTest - onRecipientChanged: function(v) { testRecipient = v } - onSubjectChanged: function(v) { testSubject = v } - onBodyChanged: function(v) { testBody = v } + onRecipientEdited: function(v) { testRecipient = v } + onSubjectEdited: function(v) { testSubject = v } + onBodyEdited: function(v) { testBody = v } onSendRequested: sendTestEmail() } diff --git a/qml/qt6/ThemeEditor.qml b/qml/qt6/ThemeEditor.qml index c38be394e..c8efdaab2 100644 --- a/qml/qt6/ThemeEditor.qml +++ b/qml/qt6/ThemeEditor.qml @@ -128,10 +128,10 @@ Rectangle { radiusSmall: root.radiusSmall radiusMedium: root.radiusMedium radiusLarge: root.radiusLarge - onBaseSpacingChanged: function(val) { root.baseSpacing = val; hasUnsavedChanges = true } - onRadiusSmallChanged: function(val) { root.radiusSmall = val; hasUnsavedChanges = true } - onRadiusMediumChanged: function(val) { root.radiusMedium = val; hasUnsavedChanges = true } - onRadiusLargeChanged: function(val) { root.radiusLarge = val; hasUnsavedChanges = true } + onBaseSpacingEdited: function(val) { root.baseSpacing = val; hasUnsavedChanges = true } + onRadiusSmallEdited: function(val) { root.radiusSmall = val; hasUnsavedChanges = true } + onRadiusMediumEdited: function(val) { root.radiusMedium = val; hasUnsavedChanges = true } + onRadiusLargeEdited: function(val) { root.radiusLarge = val; hasUnsavedChanges = true } } // Section 6: Live Preview