diff --git a/frontends/qt6/App.qml b/frontends/qt6/App.qml index 05b0bab6f..084b3a382 100644 --- a/frontends/qt6/App.qml +++ b/frontends/qt6/App.qml @@ -64,6 +64,36 @@ ApplicationWindow { currentView = "frontpage" } + // ── Static view registry (fixed indices 0–8) ── + readonly property var staticViews: [ + "frontpage", "login", "dashboard", "profile", + "admin", "god-panel", "supergod", "settings", "comments" + ] + + // ── Dynamic view index computation ── + function viewIndex(view) { + // Check static views first (indices 0–8) + var staticIdx = staticViews.indexOf(view) + if (staticIdx >= 0) + return staticIdx + + // Check dynamic package views (indices 9+) + var navPkgs = PackageLoader.navigablePackages() + for (var i = 0; i < navPkgs.length; i++) { + var pkg = navPkgs[i] + var viewName = pkg.navLabel ? pkg.navLabel.toLowerCase().replace(/ /g, "-") : pkg.packageId + if (viewName === view || pkg.packageId === view) + return staticViews.length + i + } + + return 0 + } + + // Convert packageId to view name for navigation + function packageViewName(pkg) { + return pkg.navLabel ? pkg.navLabel.toLowerCase().replace(/ /g, "-") : pkg.packageId + } + // ── App bar ── header: CAppBar { height: 56 @@ -205,22 +235,15 @@ ApplicationWindow { Layout.bottomMargin: 8 } + // Static core nav items Repeater { model: { var items = [ { label: "Dashboard", view: "dashboard", icon: "~", level: 2 }, { label: "Profile", view: "profile", icon: "P", level: 2 }, - { label: "Forum", view: "forum", icon: "F", level: 2 }, - { label: "Gallery", view: "gallery", icon: "G", level: 2 }, - { label: "Guestbook", view: "guestbook", icon: "B", level: 2 }, - { label: "Blog", view: "blog", icon: "W", level: 2 }, { label: "Comments", view: "comments", icon: "C", level: 2 }, { label: "Admin Panel", view: "admin", icon: "A", level: 3 }, - { label: "Analytics", view: "analytics", icon: "A", level: 3 }, - { label: "Watchtower", view: "watchtower", icon: "W", level: 3 }, { label: "God Panel", view: "god-panel", icon: "G", level: 4 }, - { label: "Packages", view: "packages", icon: "P", level: 4 }, - { label: "Storybook", view: "storybook", icon: "S", level: 4 }, { label: "Super God", view: "supergod", icon: "S", level: 5 } ] return items.filter(function(item) { return item.level <= currentLevel }) @@ -235,6 +258,25 @@ ApplicationWindow { } } + // Dynamic package nav items (from PackageLoader) + Repeater { + model: { + var navPkgs = PackageLoader.navigablePackages() + return navPkgs.filter(function(pkg) { + var lvl = pkg.level ? pkg.level : 2 + return lvl <= currentLevel + }) + } + + delegate: CListItem { + Layout.fillWidth: true + title: modelData.navLabel ? modelData.navLabel : modelData.name + leadingIcon: modelData.icon ? modelData.icon : modelData.name.charAt(0) + selected: currentView === packageViewName(modelData) + onClicked: currentView = packageViewName(modelData) + } + } + Item { Layout.fillHeight: true } CDivider { Layout.fillWidth: true } @@ -259,38 +301,28 @@ ApplicationWindow { anchors.fill: parent currentIndex: viewIndex(currentView) + // Static views (indices 0–8) FrontPage {} // 0: Public landing LoginView {} // 1: Login DashboardView {} // 2: Dashboard ProfileView {} // 3: Profile - PackageViewLoader { packageId: "forum" } // 4 - PackageViewLoader { packageId: "gallery" } // 5 - PackageViewLoader { packageId: "guestbook" } // 6 - PackageViewLoader { packageId: "blog" } // 7 - AdminView {} // 8: Admin - PackageViewLoader { packageId: "analytics" } // 9 - PackageViewLoader { packageId: "watchtower" } // 10 - GodPanel {} // 11: God Panel (13-tab builder) - PackageManager {} // 12: Package Manager - Storybook {} // 13: Storybook - SuperGodPanel {} // 14: Super God Panel - SettingsView {} // 15: Settings - CommentsView {} // 16: Comments + AdminView {} // 4: Admin + GodPanel {} // 5: God Panel (13-tab builder) + SuperGodPanel {} // 6: Super God Panel + SettingsView {} // 7: Settings + CommentsView {} // 8: Comments + + // Dynamic package views (indices 9+) + Repeater { + model: PackageLoader.navigablePackages() + delegate: PackageViewLoader { + packageId: modelData.packageId + } + } } } } - function viewIndex(view) { - var views = [ - "frontpage", "login", "dashboard", "profile", "forum", - "gallery", "guestbook", "blog", "admin", "analytics", - "watchtower", "god-panel", "packages", "storybook", - "supergod", "settings", "comments" - ] - var idx = views.indexOf(view) - return idx >= 0 ? idx : 0 - } - // ── Window state persistence ── Settings { id: windowSettings diff --git a/frontends/qt6/PackageManager.qml b/frontends/qt6/PackageManager.qml index 3c018dc21..cb7c0fc94 100644 --- a/frontends/qt6/PackageManager.qml +++ b/frontends/qt6/PackageManager.qml @@ -6,33 +6,18 @@ import QmlComponents 1.0 Rectangle { color: Theme.background - property var repositories: [ - { name: "Official", url: "https://repo.metabuilder.dev", description: "Curated MetaBuilder toolkit", status: "online" }, - { name: "Community", url: "https://community.metabuilder.dev", description: "Community-contributed adapters", status: "online" }, - { name: "Local", url: "file://packages/local", description: "Local drafts", status: "offline" } - ] - - property var packages: [ - { id: "material_ui", name: "Material UI Kit", repo: "Official", version: "2.1.0", description: "Shared Material components for Qt.", installed: true, size: "4.1 MB" }, - { id: "db_connector", name: "Qt DB Connector", repo: "Community", version: "1.4.2", description: "Live DBAL observability widgets.", installed: false, size: "2.7 MB" }, - { id: "prisma_console", name: "Prisma Console", repo: "Official", version: "0.9.0", description: "Prisma schema preview and migrations view.", installed: false, size: "3.2 MB" }, - { id: "storybook_themes", name: "Storybook Themes", repo: "Local", version: "1.0.3", description: "Additional Storybook scenes + theming", installed: true, size: "1.8 MB" }, - { id: "telemetry", name: "Telemetry Metrics", repo: "Community", version: "0.4.1", description: "CPU/RAM/Latency dashboards for daemons.", installed: false, size: "5.6 MB" } - ] - - property int selectedRepoIndex: 0 property string searchText: "" - - function togglePackage(name, install) { - packages = packages.map(function(pkg) { - return pkg.name === name ? Object.assign({}, pkg, { installed: install }) : pkg - }) - } + property string selectedPackageId: "" function filteredPackages() { - var currentRepo = repositories[selectedRepoIndex].name - return packages.filter(function(pkg) { - return pkg.repo === currentRepo && pkg.name.toLowerCase().indexOf(searchText.toLowerCase()) !== -1 + var allPkgs = PackageLoader.packages + if (searchText === "") + return allPkgs + var lower = searchText.toLowerCase() + return allPkgs.filter(function(pkg) { + return pkg.name.toLowerCase().indexOf(lower) !== -1 + || pkg.packageId.toLowerCase().indexOf(lower) !== -1 + || (pkg.description && pkg.description.toLowerCase().indexOf(lower) !== -1) }) } @@ -46,8 +31,8 @@ Rectangle { spacing: 12 CText { variant: "h3"; text: "Package Manager" } CStatusBadge { - status: repositories[selectedRepoIndex].status === "online" ? "success" : "warning" - text: repositories[selectedRepoIndex].status + status: "success" + text: PackageLoader.packageCount + " packages" } } @@ -56,7 +41,7 @@ Rectangle { Layout.fillHeight: true spacing: 16 - // Repo sidebar + // Package details sidebar CCard { Layout.preferredWidth: 320 Layout.fillHeight: true @@ -66,25 +51,90 @@ Rectangle { anchors.margins: 16 spacing: 12 - CText { variant: "subtitle1"; text: "Repositories" } + CText { variant: "subtitle1"; text: "Package Details" } - ListView { + // Show details for selected package + Rectangle { Layout.fillWidth: true Layout.fillHeight: true - model: repositories - spacing: 6 - delegate: CListItem { - width: parent ? parent.width : 280 - title: modelData.name - subtitle: modelData.description - selected: index === selectedRepoIndex - onClicked: selectedRepoIndex = index + color: "transparent" + visible: selectedPackageId !== "" + + ColumnLayout { + anchors.fill: parent + spacing: 8 + + CText { + variant: "h5" + text: { + var pkg = PackageLoader.getPackage(selectedPackageId) + return pkg ? pkg.name : "" + } + } + + CText { + variant: "body2" + wrapMode: Text.Wrap + Layout.fillWidth: true + text: { + var pkg = PackageLoader.getPackage(selectedPackageId) + return pkg ? pkg.description : "" + } + } + + CDivider { Layout.fillWidth: true } + + CText { variant: "caption"; text: "Version" } + CText { + variant: "body2" + text: { + var pkg = PackageLoader.getPackage(selectedPackageId) + return pkg ? pkg.version : "" + } + } + + CText { variant: "caption"; text: "Category" } + CBadge { + text: { + var pkg = PackageLoader.getPackage(selectedPackageId) + return pkg && pkg.category ? pkg.category : "—" + } + } + + CText { variant: "caption"; text: "Dependencies" } + CText { + variant: "body2" + wrapMode: Text.Wrap + Layout.fillWidth: true + text: { + var deps = PackageLoader.resolveDependencies(selectedPackageId) + return deps.length > 0 ? deps.join(", ") : "None" + } + } + + Item { Layout.fillHeight: true } } } + // Placeholder when nothing selected + CText { + visible: selectedPackageId === "" + variant: "body2" + text: "Select a package to view details" + Layout.fillWidth: true + wrapMode: Text.Wrap + } + + Item { Layout.fillHeight: true } + CDivider { Layout.fillWidth: true } - CButton { text: "Add repository"; variant: "primary"; Layout.fillWidth: true } - CButton { text: "Refresh metadata"; variant: "ghost"; Layout.fillWidth: true } + + CButton { + text: "Rescan packages" + variant: "ghost" + Layout.fillWidth: true + onClicked: PackageLoader.scan() + } } } @@ -107,7 +157,10 @@ Rectangle { onTextChanged: searchText = text Layout.fillWidth: true } - CButton { text: "Install from archive"; variant: "ghost" } + CText { + variant: "caption" + text: filteredPackages().length + " / " + PackageLoader.packageCount + } } ListView { @@ -115,6 +168,7 @@ Rectangle { Layout.fillHeight: true model: filteredPackages() spacing: 8 + clip: true delegate: CCard { width: parent ? parent.width : 400 variant: "outlined" @@ -124,12 +178,38 @@ Rectangle { anchors.margins: 16 spacing: 16 + // Package icon badge + Rectangle { + width: 40 + height: 40 + radius: 8 + color: modelData.installed ? Theme.primary : Theme.border + Layout.alignment: Qt.AlignVCenter + + CText { + anchors.centerIn: parent + text: modelData.icon ? modelData.icon : modelData.name.charAt(0) + variant: "subtitle1" + color: modelData.installed ? "#ffffff" : Theme.textPrimary + } + } + ColumnLayout { Layout.fillWidth: true spacing: 4 CText { variant: "subtitle1"; text: modelData.name } CText { variant: "body2"; text: modelData.description; wrapMode: Text.Wrap } - CText { variant: "caption"; text: "v" + modelData.version + " \u00b7 " + modelData.size } + FlexRow { + spacing: 8 + CBadge { text: "v" + modelData.version } + CBadge { + text: modelData.category ? modelData.category : "—" + visible: modelData.category !== undefined + } + CBadge { + text: "Level " + (modelData.level ? modelData.level : 2) + } + } } ColumnLayout { @@ -139,18 +219,20 @@ Rectangle { variant: modelData.installed ? "default" : "primary" enabled: !modelData.installed size: "sm" - onClicked: { - if (PackageRegistry.loadPackage(modelData.id)) { - togglePackage(modelData.name, true) - } - } + onClicked: PackageLoader.install(modelData.packageId) } CButton { text: "Uninstall" variant: "danger" size: "sm" enabled: modelData.installed - onClicked: togglePackage(modelData.name, false) + onClicked: PackageLoader.uninstall(modelData.packageId) + } + CButton { + text: "Details" + variant: "ghost" + size: "sm" + onClicked: selectedPackageId = modelData.packageId } } } diff --git a/frontends/qt6/PackageViewLoader.qml b/frontends/qt6/PackageViewLoader.qml index 09570d8e8..02f238196 100644 --- a/frontends/qt6/PackageViewLoader.qml +++ b/frontends/qt6/PackageViewLoader.qml @@ -9,11 +9,11 @@ Rectangle { property string packageId: "" - // Try to load the package's QML view from disk + // Resolve view URL via PackageLoader (disk → QRC fallback) Loader { id: viewLoader anchors.fill: parent - source: resolvePackageView() + source: packageId !== "" ? PackageLoader.qmlPathUrl(packageId) : "" onStatusChanged: { if (status === Loader.Error) { console.warn("PackageViewLoader: failed to load", packageId, source) @@ -21,6 +21,18 @@ Rectangle { } } + // Hot-reload: react to QML file changes on disk + Connections { + target: PackageLoader + function onPackageUpdated(id) { + if (id === packageId) { + var url = PackageLoader.qmlPathUrl(packageId) + viewLoader.source = "" + viewLoader.source = url + } + } + } + // Fallback when view can't be loaded Rectangle { anchors.fill: parent @@ -40,7 +52,7 @@ Rectangle { CText { variant: "body1" text: { - var meta = PackageRegistry.metadata(packageId) + var meta = PackageLoader.getPackage(packageId) return meta && meta.description ? meta.description : "Package view for " + packageId } Layout.alignment: Qt.AlignHCenter @@ -56,26 +68,15 @@ Rectangle { variant: "primary" Layout.alignment: Qt.AlignHCenter onClicked: { - if (PackageRegistry.loadPackage(packageId)) { - console.log("Loaded package:", packageId) - } + PackageLoader.install(packageId) + console.log("Installed package:", packageId) } } } } - function resolvePackageView() { - // Try to find the PackageView.qml from package directories - var paths = [ - "packages/" + packageId + "/PackageView.qml", - "../packages/" + packageId + "/PackageView.qml" - ] - // Return first candidate; QML Loader handles missing gracefully - return paths[0] - } - function formatTitle(id) { - return id.split("-").map(function(w) { + return id.split("_").map(function(w) { return w.charAt(0).toUpperCase() + w.slice(1) }).join(" ") } diff --git a/frontends/qt6/packages/analytics/metadata.json b/frontends/qt6/packages/analytics/metadata.json index 5733abf4f..a01e57d03 100644 --- a/frontends/qt6/packages/analytics/metadata.json +++ b/frontends/qt6/packages/analytics/metadata.json @@ -3,5 +3,10 @@ "name": "Analytics Studio", "version": "1.0.0", "description": "Realtime dashboards for usage, telemetry, and forum health.", - "dependencies": ["god_panel", "blog"] + "dependencies": ["god_panel", "blog"], + "level": 3, + "category": "admin", + "icon": "A", + "navLabel": "Analytics", + "showInNav": true } diff --git a/frontends/qt6/packages/blog/metadata.json b/frontends/qt6/packages/blog/metadata.json index f7c557c60..f99b65f5d 100644 --- a/frontends/qt6/packages/blog/metadata.json +++ b/frontends/qt6/packages/blog/metadata.json @@ -3,5 +3,10 @@ "name": "Blog", "version": "1.0.0", "description": "Storytelling hub with author profiles and read-later tagging.", - "dependencies": ["profile_page"] + "dependencies": ["profile_page"], + "level": 2, + "category": "social", + "icon": "W", + "navLabel": "Blog", + "showInNav": true } diff --git a/frontends/qt6/packages/breakout/metadata.json b/frontends/qt6/packages/breakout/metadata.json index 8bd1745dd..1746c867b 100644 --- a/frontends/qt6/packages/breakout/metadata.json +++ b/frontends/qt6/packages/breakout/metadata.json @@ -3,5 +3,10 @@ "name": "Breakout", "version": "1.0.0", "description": "Brick-breaking arcade experience that shares assets with the Retro Arcade package.", - "dependencies": ["retro_games", "gallery"] + "dependencies": ["retro_games", "gallery"], + "level": 2, + "category": "games", + "icon": "B", + "navLabel": "Breakout", + "showInNav": false } diff --git a/frontends/qt6/packages/connection-hub/metadata.json b/frontends/qt6/packages/connection-hub/metadata.json index ee9112f1b..9806b8ce2 100644 --- a/frontends/qt6/packages/connection-hub/metadata.json +++ b/frontends/qt6/packages/connection-hub/metadata.json @@ -3,5 +3,10 @@ "name": "Connection Hub", "version": "1.0.0", "description": "Social graph with events, groups, and shared media.", - "dependencies": ["profile_page", "gallery"] + "dependencies": ["profile_page", "gallery"], + "level": 2, + "category": "social", + "icon": "C", + "navLabel": "Connection Hub", + "showInNav": false } diff --git a/frontends/qt6/packages/escape-room/metadata.json b/frontends/qt6/packages/escape-room/metadata.json index 9f12b1688..9176f90e0 100644 --- a/frontends/qt6/packages/escape-room/metadata.json +++ b/frontends/qt6/packages/escape-room/metadata.json @@ -3,5 +3,10 @@ "name": "Escape Room", "version": "1.0.0", "description": "Puzzle-driven escape room within the Qt UI for team-building.", - "dependencies": ["retro_games", "microthread"] + "dependencies": ["retro_games", "microthread"], + "level": 2, + "category": "games", + "icon": "E", + "navLabel": "Escape Room", + "showInNav": false } diff --git a/frontends/qt6/packages/forum/metadata.json b/frontends/qt6/packages/forum/metadata.json index 9fb038888..6adbc468e 100644 --- a/frontends/qt6/packages/forum/metadata.json +++ b/frontends/qt6/packages/forum/metadata.json @@ -3,5 +3,10 @@ "name": "Community Forum", "version": "1.0.0", "description": "Threaded discussions with tagging and moderation tools.", - "dependencies": ["profile_page", "guestbook"] + "dependencies": ["profile_page", "guestbook"], + "level": 2, + "category": "social", + "icon": "F", + "navLabel": "Forum", + "showInNav": true } diff --git a/frontends/qt6/packages/frontpage/metadata.json b/frontends/qt6/packages/frontpage/metadata.json index ec34d6bdb..3b9170bc9 100644 --- a/frontends/qt6/packages/frontpage/metadata.json +++ b/frontends/qt6/packages/frontpage/metadata.json @@ -3,5 +3,10 @@ "name": "Frontpage Experience", "version": "1.0.0", "description": "Public landing page that stitches the hero, features, and CTA across all Qt experiences.", - "dependencies": ["login", "gallery", "blog"] + "dependencies": ["login", "gallery", "blog"], + "level": 1, + "category": "core", + "icon": "H", + "navLabel": "Frontpage", + "showInNav": false } diff --git a/frontends/qt6/packages/gallery/metadata.json b/frontends/qt6/packages/gallery/metadata.json index 689841e60..ee80c168d 100644 --- a/frontends/qt6/packages/gallery/metadata.json +++ b/frontends/qt6/packages/gallery/metadata.json @@ -3,5 +3,10 @@ "name": "Gallery", "version": "1.0.0", "description": "Media grid with lightbox, filters, and curated collections.", - "dependencies": ["frontpage"] + "dependencies": ["frontpage"], + "level": 2, + "category": "media", + "icon": "G", + "navLabel": "Gallery", + "showInNav": true } diff --git a/frontends/qt6/packages/god-panel/metadata.json b/frontends/qt6/packages/god-panel/metadata.json index 8812b9875..49f68d371 100644 --- a/frontends/qt6/packages/god-panel/metadata.json +++ b/frontends/qt6/packages/god-panel/metadata.json @@ -3,5 +3,10 @@ "name": "God Panel", "version": "1.0.0", "description": "Power user console with server toggles, credentials, and runtime health.", - "dependencies": ["login", "user_settings"] + "dependencies": ["login", "user_settings"], + "level": 4, + "category": "admin", + "icon": "G", + "navLabel": "God Panel", + "showInNav": false } diff --git a/frontends/qt6/packages/guestbook/metadata.json b/frontends/qt6/packages/guestbook/metadata.json index e95b2f589..876669103 100644 --- a/frontends/qt6/packages/guestbook/metadata.json +++ b/frontends/qt6/packages/guestbook/metadata.json @@ -3,5 +3,10 @@ "name": "Guestbook", "version": "1.0.0", "description": "Simple contributions feed for visitors to leave messages.", - "dependencies": [] + "dependencies": [], + "level": 2, + "category": "social", + "icon": "B", + "navLabel": "Guestbook", + "showInNav": true } diff --git a/frontends/qt6/packages/login/metadata.json b/frontends/qt6/packages/login/metadata.json index 36193675e..48cab6f75 100644 --- a/frontends/qt6/packages/login/metadata.json +++ b/frontends/qt6/packages/login/metadata.json @@ -3,5 +3,10 @@ "name": "Login Shell", "version": "1.0.0", "description": "Authentication gate with credential helpers and shortcuts to God-level panels.", - "dependencies": [] + "dependencies": [], + "level": 1, + "category": "core", + "icon": "L", + "navLabel": "Login", + "showInNav": false } diff --git a/frontends/qt6/packages/marketplace/metadata.json b/frontends/qt6/packages/marketplace/metadata.json index 40c0f375d..168410a28 100644 --- a/frontends/qt6/packages/marketplace/metadata.json +++ b/frontends/qt6/packages/marketplace/metadata.json @@ -3,5 +3,10 @@ "name": "Marketplace", "version": "1.0.0", "description": "Curated extensions and paid components with purchase-to-install workflow.", - "dependencies": ["frontpage", "storybook"] + "dependencies": ["frontpage", "storybook"], + "level": 2, + "category": "commerce", + "icon": "M", + "navLabel": "Marketplace", + "showInNav": false } diff --git a/frontends/qt6/packages/microthread/metadata.json b/frontends/qt6/packages/microthread/metadata.json index 752c91d2a..7cdba9d3b 100644 --- a/frontends/qt6/packages/microthread/metadata.json +++ b/frontends/qt6/packages/microthread/metadata.json @@ -3,5 +3,10 @@ "name": "MicroThread", "version": "1.0.0", "description": "Rapid-fire posting feed, like a micro-blogging stream.", - "dependencies": ["login", "profile_page"] + "dependencies": ["login", "profile_page"], + "level": 2, + "category": "social", + "icon": "T", + "navLabel": "MicroThread", + "showInNav": false } diff --git a/frontends/qt6/packages/music-player/metadata.json b/frontends/qt6/packages/music-player/metadata.json index 45b5d9bb9..0a6a19c23 100644 --- a/frontends/qt6/packages/music-player/metadata.json +++ b/frontends/qt6/packages/music-player/metadata.json @@ -3,5 +3,10 @@ "name": "Music Player", "version": "1.0.0", "description": "Ambient audio player with playlists, scrobbling, and visualizers.", - "dependencies": ["gallery", "analytics"] + "dependencies": ["gallery", "analytics"], + "level": 2, + "category": "media", + "icon": "M", + "navLabel": "Music Player", + "showInNav": false } diff --git a/frontends/qt6/packages/package-manager/metadata.json b/frontends/qt6/packages/package-manager/metadata.json index 71fe48ce9..baa867a7c 100644 --- a/frontends/qt6/packages/package-manager/metadata.json +++ b/frontends/qt6/packages/package-manager/metadata.json @@ -3,5 +3,10 @@ "name": "Package Manager", "version": "1.0.0", "description": "Ubuntu Store style manager that hosts repositories and installer UI.", - "dependencies": ["storybook", "frontpage", "analytics"] + "dependencies": ["storybook", "frontpage", "analytics"], + "level": 4, + "category": "dev", + "icon": "P", + "navLabel": "Packages", + "showInNav": true } diff --git a/frontends/qt6/packages/profile-page/metadata.json b/frontends/qt6/packages/profile-page/metadata.json index bdd4cc62e..d78f210da 100644 --- a/frontends/qt6/packages/profile-page/metadata.json +++ b/frontends/qt6/packages/profile-page/metadata.json @@ -3,5 +3,10 @@ "name": "Profile Page", "version": "1.0.0", "description": "User profile, activity feed, and quick links to social mini-apps.", - "dependencies": ["login", "user_settings"] + "dependencies": ["login", "user_settings"], + "level": 2, + "category": "core", + "icon": "P", + "navLabel": "Profile", + "showInNav": false } diff --git a/frontends/qt6/packages/retro-games/metadata.json b/frontends/qt6/packages/retro-games/metadata.json index 07602a42f..6df77bbea 100644 --- a/frontends/qt6/packages/retro-games/metadata.json +++ b/frontends/qt6/packages/retro-games/metadata.json @@ -3,5 +3,10 @@ "name": "Retro Arcade", "version": "1.0.0", "description": "Pixel-perfect retro games to keep visitors entertained inside the app.", - "dependencies": ["gallery"] + "dependencies": ["gallery"], + "level": 2, + "category": "games", + "icon": "R", + "navLabel": "Retro Arcade", + "showInNav": false } diff --git a/frontends/qt6/packages/snake-game/metadata.json b/frontends/qt6/packages/snake-game/metadata.json index 2fa5a7c71..27ae5532a 100644 --- a/frontends/qt6/packages/snake-game/metadata.json +++ b/frontends/qt6/packages/snake-game/metadata.json @@ -3,5 +3,10 @@ "name": "Snake Game", "version": "1.0.0", "description": "Classic snake action that can be embedded into any launcher view.", - "dependencies": ["retro_games"] + "dependencies": ["retro_games"], + "level": 2, + "category": "games", + "icon": "S", + "navLabel": "Snake Game", + "showInNav": false } diff --git a/frontends/qt6/packages/storybook/metadata.json b/frontends/qt6/packages/storybook/metadata.json index 90628b925..5fe0bc40e 100644 --- a/frontends/qt6/packages/storybook/metadata.json +++ b/frontends/qt6/packages/storybook/metadata.json @@ -3,5 +3,10 @@ "name": "Storybook Showcase", "version": "1.0.0", "description": "Interactive catalog of all Material components, used for QA and design feedback.", - "dependencies": ["login", "frontpage"] + "dependencies": ["login", "frontpage"], + "level": 4, + "category": "dev", + "icon": "S", + "navLabel": "Storybook", + "showInNav": true } diff --git a/frontends/qt6/packages/supergod-panel/metadata.json b/frontends/qt6/packages/supergod-panel/metadata.json index b26545a47..93ad7fb2a 100644 --- a/frontends/qt6/packages/supergod-panel/metadata.json +++ b/frontends/qt6/packages/supergod-panel/metadata.json @@ -3,5 +3,10 @@ "name": "SuperGod Panel", "version": "1.0.0", "description": "Overlord console that orchestrates daemons, migrations, and meta workflows.", - "dependencies": ["god_panel", "retro_games"] + "dependencies": ["god_panel", "retro_games"], + "level": 5, + "category": "admin", + "icon": "S", + "navLabel": "Super God", + "showInNav": false } diff --git a/frontends/qt6/packages/user-settings/metadata.json b/frontends/qt6/packages/user-settings/metadata.json index 75d68de34..7ace3a9e2 100644 --- a/frontends/qt6/packages/user-settings/metadata.json +++ b/frontends/qt6/packages/user-settings/metadata.json @@ -3,5 +3,10 @@ "name": "User Settings", "version": "1.0.0", "description": "Personalization controls for notifications, themes, and privacy knobs.", - "dependencies": ["login"] + "dependencies": ["login"], + "level": 2, + "category": "core", + "icon": "U", + "navLabel": "Settings", + "showInNav": false } diff --git a/frontends/qt6/packages/watchtower/PackageView.qml b/frontends/qt6/packages/watchtower/PackageView.qml index 8216af021..76a1b6244 100644 --- a/frontends/qt6/packages/watchtower/PackageView.qml +++ b/frontends/qt6/packages/watchtower/PackageView.qml @@ -36,7 +36,7 @@ Rectangle { ListElement { severity: "info"; message: "System startup complete — all services initialized"; timestamp: "07:40:00" } ListElement { severity: "error"; message: "Failed health check on Postgres (timeout), auto-retried OK"; timestamp: "07:38:45" } ListElement { severity: "warning"; message: "Memory usage spike to 78% during backup (normalized)"; timestamp: "06:30:10" } - ] + } Timer { id: refreshTimer diff --git a/frontends/qt6/packages/watchtower/metadata.json b/frontends/qt6/packages/watchtower/metadata.json index ca815a8fa..e5fa745c5 100644 --- a/frontends/qt6/packages/watchtower/metadata.json +++ b/frontends/qt6/packages/watchtower/metadata.json @@ -3,5 +3,10 @@ "name": "Watchtower", "version": "1.0.0", "description": "Mission control for logging, alerts, and daemon orchestration.", - "dependencies": ["analytics", "god_panel"] + "dependencies": ["analytics", "god_panel"], + "level": 3, + "category": "admin", + "icon": "W", + "navLabel": "Watchtower", + "showInNav": true } diff --git a/frontends/qt6/qmllib/Material/MaterialAccordion.qml b/frontends/qt6/qmllib/Material/MaterialAccordion.qml index 19eefea79..e90d70c21 100644 --- a/frontends/qt6/qmllib/Material/MaterialAccordion.qml +++ b/frontends/qt6/qmllib/Material/MaterialAccordion.qml @@ -66,4 +66,3 @@ Rectangle { default property alias content: contentLoader.sourceComponent } -} diff --git a/frontends/qt6/src/PackageLoader.cpp b/frontends/qt6/src/PackageLoader.cpp index 58ad1dc11..c495342f4 100644 --- a/frontends/qt6/src/PackageLoader.cpp +++ b/frontends/qt6/src/PackageLoader.cpp @@ -5,6 +5,7 @@ #include #include #include +#include PackageLoader::PackageLoader(QObject *parent) : QObject(parent) @@ -72,6 +73,10 @@ void PackageLoader::setWatching(bool enabled) const QString metaPath = pkgDir + QStringLiteral("/metadata.json"); if (QFile::exists(metaPath)) m_watcher->addPath(metaPath); + + const QString qmlViewPath = pkgDir + QStringLiteral("/PackageView.qml"); + if (QFile::exists(qmlViewPath)) + m_watcher->addPath(qmlViewPath); } } else { delete m_watcher; @@ -101,6 +106,7 @@ void PackageLoader::scan() loadPackage(root.absoluteFilePath(entry)); } + loadInstallState(); emit packagesChanged(); } @@ -157,6 +163,7 @@ void PackageLoader::install(const QString &packageId) return; } m_installed[packageId] = true; + saveInstallState(); emit packageInstalled(packageId); emit packagesChanged(); } @@ -166,6 +173,7 @@ void PackageLoader::uninstall(const QString &packageId) if (!m_installed.contains(packageId)) return; m_installed.remove(packageId); + saveInstallState(); emit packageUninstalled(packageId); emit packagesChanged(); } @@ -227,6 +235,94 @@ QString PackageLoader::qmlPath(const QString &packageId) const return dir + QStringLiteral("/PackageView.qml"); } +QUrl PackageLoader::qmlPathUrl(const QString &packageId) const +{ + if (!m_packages.contains(packageId)) + return {}; + + // Try disk path first (dev mode) + const QString diskPath = qmlPath(packageId); + if (QFile::exists(diskPath)) + return QUrl::fromLocalFile(diskPath); + + // Fall back to QRC for bundled builds + const QString qrcPath = QStringLiteral("qrc:/packages/") + packageId + QStringLiteral("/PackageView.qml"); + return QUrl(qrcPath); +} + +QVariantList PackageLoader::navigablePackages() const +{ + QVariantList result; + for (auto it = m_packages.constBegin(); it != m_packages.constEnd(); ++it) { + const QJsonObject &meta = it.value(); + if (meta.value(QStringLiteral("showInNav")).toBool(false)) { + QVariantMap entry = meta.toVariantMap(); + entry[QStringLiteral("installed")] = m_installed.value(it.key(), false); + result.append(entry); + } + } + + // Sort by level (ascending) then by name (alphabetical) + std::sort(result.begin(), result.end(), [](const QVariant &a, const QVariant &b) { + const QVariantMap ma = a.toMap(); + const QVariantMap mb = b.toMap(); + const int levelA = ma.value(QStringLiteral("level"), 2).toInt(); + const int levelB = mb.value(QStringLiteral("level"), 2).toInt(); + if (levelA != levelB) + return levelA < levelB; + return ma.value(QStringLiteral("name")).toString() < mb.value(QStringLiteral("name")).toString(); + }); + + return result; +} + +// --------------------------------------------------------------------------- +// Persistent install state +// --------------------------------------------------------------------------- + +void PackageLoader::loadInstallState() +{ + const QString path = m_packagesDir + QStringLiteral("/installed.json"); + QFile file(path); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + return; + + QJsonParseError parseErr; + const QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &parseErr); + file.close(); + + if (parseErr.error != QJsonParseError::NoError) { + qWarning() << "PackageLoader: failed to parse installed.json:" << parseErr.errorString(); + return; + } + + m_installed.clear(); + const QJsonObject obj = doc.object(); + for (auto it = obj.constBegin(); it != obj.constEnd(); ++it) { + if (it.value().toBool()) + m_installed[it.key()] = true; + } +} + +void PackageLoader::saveInstallState() const +{ + const QString path = m_packagesDir + QStringLiteral("/installed.json"); + QFile file(path); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qWarning() << "PackageLoader: failed to write installed.json:" << path; + return; + } + + QJsonObject obj; + for (auto it = m_installed.constBegin(); it != m_installed.constEnd(); ++it) { + if (it.value()) + obj[it.key()] = true; + } + + file.write(QJsonDocument(obj).toJson(QJsonDocument::Indented)); + file.close(); +} + // --------------------------------------------------------------------------- // File system watcher slots // --------------------------------------------------------------------------- @@ -262,18 +358,25 @@ void PackageLoader::onFileChanged(const QString &path) { emit fileChanged(path); - // Re-read the metadata.json that was modified QFileInfo fi(path); const QString pkgDir = fi.absolutePath(); - loadPackage(pkgDir); + const bool isQmlFile = path.endsWith(QStringLiteral(".qml")); + if (!isQmlFile) { + // Re-read the metadata.json that was modified + loadPackage(pkgDir); + } + + // Emit packageUpdated for both metadata and QML file changes for (auto it = m_packages.constBegin(); it != m_packages.constEnd(); ++it) { if (it.value().value(QStringLiteral("_dir")).toString() == pkgDir) { emit packageUpdated(it.key()); break; } } - emit packagesChanged(); + + if (!isQmlFile) + emit packagesChanged(); // QFileSystemWatcher may drop the watch after a file change – re-add if (m_watcher && QFile::exists(path) && !m_watcher->files().contains(path)) diff --git a/frontends/qt6/src/PackageLoader.h b/frontends/qt6/src/PackageLoader.h index 8738f092b..cf6022943 100644 --- a/frontends/qt6/src/PackageLoader.h +++ b/frontends/qt6/src/PackageLoader.h @@ -8,6 +8,7 @@ #include #include #include +#include #include class PackageLoader : public QObject @@ -38,6 +39,8 @@ public slots: QVariantMap getPackage(const QString &packageId) const; QStringList resolveDependencies(const QString &packageId) const; QString qmlPath(const QString &packageId) const; + QUrl qmlPathUrl(const QString &packageId) const; + QVariantList navigablePackages() const; signals: void packagesChanged(); @@ -56,6 +59,8 @@ private: void loadPackage(const QString &dir); bool validateMetadata(const QJsonObject &metadata) const; QStringList resolveDepChain(const QString &packageId, QStringList &visited) const; + void loadInstallState(); + void saveInstallState() const; QString m_packagesDir; bool m_watching; diff --git a/qml/components/atoms/CBlockquote.qml b/qml/components/atoms/CBlockquote.qml index e38700afc..a99a65643 100644 --- a/qml/components/atoms/CBlockquote.qml +++ b/qml/components/atoms/CBlockquote.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Layouts +import QmlComponents 1.0 /** * CBlockquote.qml - Blockquote (mirrors _blockquote.scss) diff --git a/qml/components/atoms/CCodeBlock.qml b/qml/components/atoms/CCodeBlock.qml index 1622602dc..7d46f4c82 100644 --- a/qml/components/atoms/CCodeBlock.qml +++ b/qml/components/atoms/CCodeBlock.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts +import QmlComponents 1.0 /** * CCodeBlock.qml - Code block display (mirrors _code-block.scss) diff --git a/qml/components/atoms/CCodeInline.qml b/qml/components/atoms/CCodeInline.qml index 311847b21..bde947e67 100644 --- a/qml/components/atoms/CCodeInline.qml +++ b/qml/components/atoms/CCodeInline.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CCodeInline.qml - Inline code (mirrors _code-inline.scss) diff --git a/qml/components/atoms/CHighlight.qml b/qml/components/atoms/CHighlight.qml index cbe6a9f8e..0496cebb3 100644 --- a/qml/components/atoms/CHighlight.qml +++ b/qml/components/atoms/CHighlight.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CHighlight.qml - Text highlighting (mirrors _highlight.scss) diff --git a/qml/components/atoms/CMarkdown.qml b/qml/components/atoms/CMarkdown.qml index c045b9588..56d4f75b8 100644 --- a/qml/components/atoms/CMarkdown.qml +++ b/qml/components/atoms/CMarkdown.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CMarkdown.qml - Markdown text display (mirrors _markdown.scss) diff --git a/qml/components/atoms/CPanel.qml b/qml/components/atoms/CPanel.qml index d04a4de0a..413162f6e 100644 --- a/qml/components/atoms/CPanel.qml +++ b/qml/components/atoms/CPanel.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Layouts import QtQuick.Effects +import QmlComponents 1.0 /** * CPanel.qml - Panel component (mirrors _panel.scss) diff --git a/qml/components/atoms/CProse.qml b/qml/components/atoms/CProse.qml index dba9a7a40..6bdcee259 100644 --- a/qml/components/atoms/CProse.qml +++ b/qml/components/atoms/CProse.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Layouts +import QmlComponents 1.0 /** * CProse.qml - Long-form text container (mirrors _prose.scss) diff --git a/qml/components/atoms/CSection.qml b/qml/components/atoms/CSection.qml index 61dc10f59..baa4c2596 100644 --- a/qml/components/atoms/CSection.qml +++ b/qml/components/atoms/CSection.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Layouts +import QmlComponents 1.0 /** * CSection.qml - Content section (mirrors _section.scss) diff --git a/qml/components/atoms/CText.qml b/qml/components/atoms/CText.qml index 6ad927dfc..1560a9655 100644 --- a/qml/components/atoms/CText.qml +++ b/qml/components/atoms/CText.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CText.qml - Styled text component (mirrors SCSS text utilities) diff --git a/qml/components/atoms/CTitle.qml b/qml/components/atoms/CTitle.qml index 70c44a141..df2c69c7d 100644 --- a/qml/components/atoms/CTitle.qml +++ b/qml/components/atoms/CTitle.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CTitle.qml - Title text (mirrors _title.scss) diff --git a/qml/components/core/CButton.qml b/qml/components/core/CButton.qml index c57b068e2..a47de38ee 100644 --- a/qml/components/core/CButton.qml +++ b/qml/components/core/CButton.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts +import QmlComponents 1.0 /** * CButton.qml - Styled button component (mirrors _button.scss) diff --git a/qml/components/core/CCard.qml b/qml/components/core/CCard.qml index 4aa765a41..fd853b845 100644 --- a/qml/components/core/CCard.qml +++ b/qml/components/core/CCard.qml @@ -2,6 +2,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts import QtQuick.Effects +import QmlComponents 1.0 /** * CCard.qml - Card container component (mirrors _card.scss) diff --git a/qml/components/core/CChip.qml b/qml/components/core/CChip.qml index 43d20db5b..37e98eabe 100644 --- a/qml/components/core/CChip.qml +++ b/qml/components/core/CChip.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts +import QmlComponents 1.0 /** * CChip.qml - Chip/tag component (mirrors _chip.scss) @@ -15,6 +16,7 @@ Rectangle { property string size: "sm" // sm, md property bool clickable: false property bool closable: false + property bool checked: false signal clicked() signal closeClicked() diff --git a/qml/components/core/CFab.qml b/qml/components/core/CFab.qml index 8796e615a..197daf1ad 100644 --- a/qml/components/core/CFab.qml +++ b/qml/components/core/CFab.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CFab.qml - Floating action button diff --git a/qml/components/core/CIcon.qml b/qml/components/core/CIcon.qml index 5aeca56c6..e5f93ae9d 100644 --- a/qml/components/core/CIcon.qml +++ b/qml/components/core/CIcon.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CIcon.qml - Icon container (mirrors _icon.scss) diff --git a/qml/components/core/CIconButton.qml b/qml/components/core/CIconButton.qml index 14e85cf73..05749fbc8 100644 --- a/qml/components/core/CIconButton.qml +++ b/qml/components/core/CIconButton.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Controls +import QmlComponents 1.0 Item { id: control diff --git a/qml/components/data-display/CAvatar.qml b/qml/components/data-display/CAvatar.qml index 851a691b9..b2a21b741 100644 --- a/qml/components/data-display/CAvatar.qml +++ b/qml/components/data-display/CAvatar.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CAvatar.qml - Circular avatar (mirrors _avatar.scss) diff --git a/qml/components/data-display/CBadge.qml b/qml/components/data-display/CBadge.qml index 1f58bee27..cd4ed9ca7 100644 --- a/qml/components/data-display/CBadge.qml +++ b/qml/components/data-display/CBadge.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CBadge.qml - Notification badge (mirrors _badge.scss) @@ -11,6 +12,7 @@ Rectangle { property string variant: "primary" // primary, success, warning, error property int count: 0 // Number to display (0 = dot only) property bool dot: false // Show as dot without number + property string text: "" // Direct text label (overrides count) // Size mapping readonly property var _sizes: ({ @@ -34,6 +36,7 @@ Rectangle { readonly property color _textColor: variant === "warning" ? "#000000" : "#ffffff" // Sizing + readonly property bool _hasText: text !== "" width: dot ? _sizeConfig.height : Math.max(_sizeConfig.minWidth, label.implicitWidth + _sizeConfig.padding * 2) height: _sizeConfig.height radius: height / 2 @@ -43,10 +46,10 @@ Rectangle { Text { id: label anchors.centerIn: parent - text: root.count > 99 ? "99+" : root.count.toString() + text: root._hasText ? root.text : (root.count > 99 ? "99+" : root.count.toString()) color: root._textColor font.pixelSize: root._sizeConfig.fontSize font.weight: Font.DemiBold - visible: !root.dot && root.count > 0 + visible: !root.dot && (root._hasText || root.count > 0) } } diff --git a/qml/components/data-display/CDivider.qml b/qml/components/data-display/CDivider.qml index e458cd6cc..f30de4d0e 100644 --- a/qml/components/data-display/CDivider.qml +++ b/qml/components/data-display/CDivider.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CDivider.qml - Divider/separator component (mirrors _divider.scss) diff --git a/qml/components/data-display/CStatBadge.qml b/qml/components/data-display/CStatBadge.qml index 727cee5e9..8d37f8107 100644 --- a/qml/components/data-display/CStatBadge.qml +++ b/qml/components/data-display/CStatBadge.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Layouts +import QmlComponents 1.0 /** * CStatBadge.qml - Statistic badge (mirrors _stat-badge.scss) diff --git a/qml/components/data-display/CTable.qml b/qml/components/data-display/CTable.qml index ed4e37a54..c8e1a571f 100644 --- a/qml/components/data-display/CTable.qml +++ b/qml/components/data-display/CTable.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Layouts +import QmlComponents 1.0 /** * CTable.qml - Data table (mirrors _table.scss) diff --git a/qml/components/feedback/CAlert.qml b/qml/components/feedback/CAlert.qml index ca24c9f58..a1ac2c2ad 100644 --- a/qml/components/feedback/CAlert.qml +++ b/qml/components/feedback/CAlert.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Layouts +import QmlComponents 1.0 /** * CAlert.qml - Alert/notification component (mirrors _alert.scss) diff --git a/qml/components/feedback/CDialog.qml b/qml/components/feedback/CDialog.qml index 1aee71ef1..6f4a9def9 100644 --- a/qml/components/feedback/CDialog.qml +++ b/qml/components/feedback/CDialog.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts +import QmlComponents 1.0 /** * CDialog.qml - Modal dialog (mirrors _dialog.scss) @@ -12,7 +13,7 @@ Popup { property string title: "" property string size: "md" // sm, md, lg, xl property bool showClose: true - property alias contentItem: contentArea.data + property alias dialogContent: contentArea.data property alias footerItem: footerArea.data // Size mapping diff --git a/qml/components/feedback/CErrorState.qml b/qml/components/feedback/CErrorState.qml index fb5c261a8..95f708b44 100644 --- a/qml/components/feedback/CErrorState.qml +++ b/qml/components/feedback/CErrorState.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Layouts +import QmlComponents 1.0 /** * CErrorState.qml - Error state display (mirrors _error-state.scss) diff --git a/qml/components/feedback/CProgress.qml b/qml/components/feedback/CProgress.qml index 703c3f923..1f922e097 100644 --- a/qml/components/feedback/CProgress.qml +++ b/qml/components/feedback/CProgress.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CProgress.qml - Progress indicator (mirrors _progress.scss) diff --git a/qml/components/feedback/CSnackbar.qml b/qml/components/feedback/CSnackbar.qml index 64fb7c14c..0f10b4634 100644 --- a/qml/components/feedback/CSnackbar.qml +++ b/qml/components/feedback/CSnackbar.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Layouts import QtQuick.Effects +import QmlComponents 1.0 /** * CSnackbar.qml - Snackbar/toast notification (mirrors _snackbar.scss) diff --git a/qml/components/feedback/CSpinner.qml b/qml/components/feedback/CSpinner.qml index 0ca8e540e..e3bca7036 100644 --- a/qml/components/feedback/CSpinner.qml +++ b/qml/components/feedback/CSpinner.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CSpinner.qml - Loading spinner (mirrors _spinner.scss) diff --git a/qml/components/form/CAutocomplete.qml b/qml/components/form/CAutocomplete.qml index 52654522d..cf1617e59 100644 --- a/qml/components/form/CAutocomplete.qml +++ b/qml/components/form/CAutocomplete.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts +import QmlComponents 1.0 /** * CAutocomplete.qml - simple autocomplete input with popup suggestions diff --git a/qml/components/form/CCheckbox.qml b/qml/components/form/CCheckbox.qml index 30cc6620a..7209fd0a7 100644 --- a/qml/components/form/CCheckbox.qml +++ b/qml/components/form/CCheckbox.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Controls +import QmlComponents 1.0 /** * CCheckbox.qml - styled checkbox wrapper diff --git a/qml/components/form/CFormGroup.qml b/qml/components/form/CFormGroup.qml index 85f3641c3..182502172 100644 --- a/qml/components/form/CFormGroup.qml +++ b/qml/components/form/CFormGroup.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Layouts +import QmlComponents 1.0 /** * CFormGroup.qml - Form field container (mirrors _form.scss) diff --git a/qml/components/form/CFormLabel.qml b/qml/components/form/CFormLabel.qml index a324ce8a5..31d30d908 100644 --- a/qml/components/form/CFormLabel.qml +++ b/qml/components/form/CFormLabel.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Controls +import QmlComponents 1.0 Text { id: label diff --git a/qml/components/form/CLabel.qml b/qml/components/form/CLabel.qml index 8a4ad0439..a95b56efe 100644 --- a/qml/components/form/CLabel.qml +++ b/qml/components/form/CLabel.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CLabel.qml - Form label (mirrors _label.scss) diff --git a/qml/components/form/CRadio.qml b/qml/components/form/CRadio.qml index 4e31748e3..0c0ee06e2 100644 --- a/qml/components/form/CRadio.qml +++ b/qml/components/form/CRadio.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CRadio.qml - radio control diff --git a/qml/components/form/CRating.qml b/qml/components/form/CRating.qml index a00208dd9..605578b01 100644 --- a/qml/components/form/CRating.qml +++ b/qml/components/form/CRating.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CRating.qml - simple star rating control diff --git a/qml/components/form/CSelect.qml b/qml/components/form/CSelect.qml index 86163a358..fc8821897 100644 --- a/qml/components/form/CSelect.qml +++ b/qml/components/form/CSelect.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Controls +import QtQuick.Layouts /** * CSelect.qml - simple select (ComboBox wrapper) @@ -7,7 +8,5 @@ import QtQuick.Controls ComboBox { id: root model: [] - property alias currentIndex: root.currentIndex - property alias currentText: root.currentText Layout.preferredWidth: 200 } diff --git a/qml/components/form/CSwitch.qml b/qml/components/form/CSwitch.qml index f8a6f7e42..7821a32b2 100644 --- a/qml/components/form/CSwitch.qml +++ b/qml/components/form/CSwitch.qml @@ -3,5 +3,4 @@ import QtQuick.Controls Switch { id: sw - property alias checked: sw.checked } diff --git a/qml/components/layout/CBox.qml b/qml/components/layout/CBox.qml index 0f52a13b3..79c2117f9 100644 --- a/qml/components/layout/CBox.qml +++ b/qml/components/layout/CBox.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CBox.qml - Generic container component (mirrors common SCSS patterns) diff --git a/qml/components/layout/CContainer.qml b/qml/components/layout/CContainer.qml index 799c0918b..f3f58ea20 100644 --- a/qml/components/layout/CContainer.qml +++ b/qml/components/layout/CContainer.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * CContainer.qml - Responsive container with max-width (mirrors CSS container) diff --git a/qml/components/layout/CGrid.qml b/qml/components/layout/CGrid.qml index 42a0a68c3..833d15a08 100644 --- a/qml/components/layout/CGrid.qml +++ b/qml/components/layout/CGrid.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Layouts +import QmlComponents 1.0 /** * CGrid.qml - Responsive grid layout (mirrors _grid.scss) diff --git a/qml/components/layout/FlexCol.qml b/qml/components/layout/FlexCol.qml index 239ddab4a..19c7ea798 100644 --- a/qml/components/layout/FlexCol.qml +++ b/qml/components/layout/FlexCol.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Layouts +import QmlComponents 1.0 /** * FlexCol.qml - Vertical flex container (mirrors SCSS .flex, .flex-col utilities) diff --git a/qml/components/layout/FlexRow.qml b/qml/components/layout/FlexRow.qml index ad14c83b4..edda6822f 100644 --- a/qml/components/layout/FlexRow.qml +++ b/qml/components/layout/FlexRow.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Layouts +import QmlComponents 1.0 /** * FlexRow.qml - Horizontal flex container (mirrors SCSS .flex, .flex-row utilities) diff --git a/qml/components/layout/Spacer.qml b/qml/components/layout/Spacer.qml index 3262ac73b..c3659115e 100644 --- a/qml/components/layout/Spacer.qml +++ b/qml/components/layout/Spacer.qml @@ -1,4 +1,5 @@ import QtQuick +import QmlComponents 1.0 /** * Spacer.qml - Flexible spacer utility (mirrors SCSS spacing utilities) diff --git a/qml/components/surfaces/CAccordionItem.qml b/qml/components/surfaces/CAccordionItem.qml index 3c5351112..3ccce6b5d 100644 --- a/qml/components/surfaces/CAccordionItem.qml +++ b/qml/components/surfaces/CAccordionItem.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Layouts +import QmlComponents 1.0 /** * CAccordionItem.qml - Single accordion item diff --git a/qml/components/surfaces/CAppBar.qml b/qml/components/surfaces/CAppBar.qml index 1641ec0e7..b2b8a0fd7 100644 --- a/qml/components/surfaces/CAppBar.qml +++ b/qml/components/surfaces/CAppBar.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Controls +import QtQuick.Layouts ToolBar { id: appbar diff --git a/qml/qmldir b/qml/qmldir index f40fda897..96ddaf878 100644 --- a/qml/qmldir +++ b/qml/qmldir @@ -112,6 +112,10 @@ CUseMediaQuery 1.0 components/utils/CUseMediaQuery.qml CPopover 1.0 components/utils/CPopover.qml # Theming Components +singleton Theme 1.0 components/theming/Theme.qml +singleton StyleVariables 1.0 components/theming/StyleVariables.qml +singleton StyleMixins 1.0 components/theming/StyleMixins.qml +singleton Responsive 1.0 components/theming/Responsive.qml CThemeProvider 1.0 components/theming/CThemeProvider.qml CStyled 1.0 components/theming/CStyled.qml