mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
feat(qt6): package metadata updates, QmlComponents library enhancements, PackageLoader improvements
- Update 22 package metadata.json files with consistent schema - Enhance PackageLoader with file watching and navigable packages - Update QmlComponents library (atoms, core, data-display, feedback, form, layout, surfaces) - Improve watchtower PackageView and MaterialAccordion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(" ")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -66,4 +66,3 @@ Rectangle {
|
||||
|
||||
default property alias content: contentLoader.sourceComponent
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QDebug>
|
||||
#include <algorithm>
|
||||
|
||||
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))
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <QStringList>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QMap>
|
||||
#include <QUrl>
|
||||
#include <QVariantList>
|
||||
|
||||
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;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CBlockquote.qml - Blockquote (mirrors _blockquote.scss)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CCodeInline.qml - Inline code (mirrors _code-inline.scss)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CHighlight.qml - Text highlighting (mirrors _highlight.scss)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CMarkdown.qml - Markdown text display (mirrors _markdown.scss)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CPanel.qml - Panel component (mirrors _panel.scss)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CProse.qml - Long-form text container (mirrors _prose.scss)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CSection.qml - Content section (mirrors _section.scss)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CText.qml - Styled text component (mirrors SCSS text utilities)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CTitle.qml - Title text (mirrors _title.scss)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CButton.qml - Styled button component (mirrors _button.scss)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CFab.qml - Floating action button
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CIcon.qml - Icon container (mirrors _icon.scss)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QmlComponents 1.0
|
||||
|
||||
Item {
|
||||
id: control
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CAvatar.qml - Circular avatar (mirrors _avatar.scss)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CDivider.qml - Divider/separator component (mirrors _divider.scss)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CStatBadge.qml - Statistic badge (mirrors _stat-badge.scss)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CTable.qml - Data table (mirrors _table.scss)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CAlert.qml - Alert/notification component (mirrors _alert.scss)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CErrorState.qml - Error state display (mirrors _error-state.scss)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CProgress.qml - Progress indicator (mirrors _progress.scss)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CSnackbar.qml - Snackbar/toast notification (mirrors _snackbar.scss)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CSpinner.qml - Loading spinner (mirrors _spinner.scss)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CAutocomplete.qml - simple autocomplete input with popup suggestions
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CCheckbox.qml - styled checkbox wrapper
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CFormGroup.qml - Form field container (mirrors _form.scss)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QmlComponents 1.0
|
||||
|
||||
Text {
|
||||
id: label
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CLabel.qml - Form label (mirrors _label.scss)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CRadio.qml - radio control
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CRating.qml - simple star rating control
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -3,5 +3,4 @@ import QtQuick.Controls
|
||||
|
||||
Switch {
|
||||
id: sw
|
||||
property alias checked: sw.checked
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CBox.qml - Generic container component (mirrors common SCSS patterns)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CContainer.qml - Responsive container with max-width (mirrors CSS container)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CGrid.qml - Responsive grid layout (mirrors _grid.scss)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* FlexCol.qml - Vertical flex container (mirrors SCSS .flex, .flex-col utilities)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* FlexRow.qml - Horizontal flex container (mirrors SCSS .flex, .flex-row utilities)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* Spacer.qml - Flexible spacer utility (mirrors SCSS spacing utilities)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CAccordionItem.qml - Single accordion item
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
ToolBar {
|
||||
id: appbar
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user