mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
- Created CODE_REVIEW.md for comprehensive code review documentation. - Updated 44 QML files to use modern versionless imports for QtQuick and QtQuick.Controls. - Removed debug console.log statements from App.qml for cleaner code. - Identified and documented minimal component implementations in CAutoGrid.qml and CEditorWrapper.qml, recommending enhancements for production readiness.
469 lines
17 KiB
QML
469 lines
17 KiB
QML
import QtQuick
|
||
import QtQuick.Controls
|
||
import QtQuick.Layouts
|
||
import "components"
|
||
import "contexts"
|
||
import "fakemui"
|
||
|
||
/**
|
||
* App.qml - Main application component
|
||
* Mirrors React's App.jsx with drawer navigation and view management
|
||
*/
|
||
ApplicationWindow {
|
||
id: appWindow
|
||
|
||
visible: true
|
||
width: 1280
|
||
height: 800
|
||
minimumWidth: 800
|
||
minimumHeight: 600
|
||
title: "Codex Runner"
|
||
color: Theme.background
|
||
|
||
// Initialize Responsive singleton with this window
|
||
Component.onCompleted: {
|
||
Responsive.targetWindow = appWindow
|
||
app.statusMessage.connect(onStatusMessage)
|
||
}
|
||
|
||
// Current view state
|
||
property string currentView: "tasks"
|
||
property int selectedTaskIndex: -1
|
||
property bool searchOpen: false
|
||
|
||
// Drawer width - responsive!
|
||
readonly property int drawerWidth: Responsive.isMobile ? 0 : (Responsive.isTablet ? 200 : 240)
|
||
|
||
function onStatusMessage(msg) {
|
||
// Could show a snackbar/toast here
|
||
// Status messages handled via UI feedback
|
||
}
|
||
|
||
// Keyboard shortcut: Cmd/Ctrl+K for search
|
||
Shortcut {
|
||
sequence: StandardKey.Find
|
||
onActivated: searchOpen = true
|
||
}
|
||
|
||
Shortcut {
|
||
sequence: "Ctrl+K"
|
||
onActivated: searchOpen = true
|
||
}
|
||
|
||
// Navigation
|
||
function navigateTo(view) {
|
||
currentView = view
|
||
selectedTaskIndex = -1
|
||
}
|
||
|
||
function selectTask(index) {
|
||
selectedTaskIndex = index
|
||
currentView = "taskDetail"
|
||
// Load the task detail via Python controller
|
||
app.loadTaskDetail(index)
|
||
}
|
||
|
||
// Layout: Drawer + Main Content
|
||
RowLayout {
|
||
anchors.fill: parent
|
||
spacing: 0
|
||
|
||
// Sidebar / Drawer
|
||
Rectangle {
|
||
id: drawer
|
||
Layout.preferredWidth: drawerWidth
|
||
Layout.fillHeight: true
|
||
color: Theme.paper
|
||
|
||
ColumnLayout {
|
||
anchors.fill: parent
|
||
spacing: 0
|
||
|
||
// Header
|
||
Rectangle {
|
||
Layout.fillWidth: true
|
||
Layout.preferredHeight: 64
|
||
color: "transparent"
|
||
|
||
Text {
|
||
anchors.centerIn: parent
|
||
text: "Codex Runner"
|
||
font.pixelSize: 18
|
||
font.bold: true
|
||
color: Theme.primary
|
||
}
|
||
}
|
||
|
||
// Divider
|
||
Rectangle {
|
||
Layout.fillWidth: true
|
||
Layout.preferredHeight: 1
|
||
color: Qt.rgba(Theme.text.r, Theme.text.g, Theme.text.b, 0.12)
|
||
}
|
||
|
||
// Navigation Items
|
||
ListView {
|
||
Layout.fillWidth: true
|
||
Layout.preferredHeight: contentHeight
|
||
interactive: false
|
||
|
||
model: ListModel {
|
||
ListElement { viewId: "tasks"; emoji: "☰"; labelKey: "tasks" }
|
||
ListElement { viewId: "newPrompt"; emoji: "+"; labelKey: "newTask" }
|
||
}
|
||
|
||
delegate: ItemDelegate {
|
||
width: parent.width
|
||
height: 48
|
||
highlighted: currentView === viewId
|
||
|
||
contentItem: RowLayout {
|
||
spacing: 16
|
||
|
||
Text {
|
||
text: emoji
|
||
font.pixelSize: 20
|
||
color: Theme.text
|
||
Layout.leftMargin: 16
|
||
}
|
||
|
||
Text {
|
||
text: LanguageContext.t(labelKey)
|
||
font.pixelSize: 14
|
||
color: Theme.text
|
||
}
|
||
}
|
||
|
||
background: Rectangle {
|
||
color: highlighted ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
||
: (hovered ? Qt.rgba(Theme.text.r, Theme.text.g, Theme.text.b, 0.04) : "transparent")
|
||
}
|
||
|
||
onClicked: navigateTo(viewId)
|
||
}
|
||
}
|
||
|
||
// Divider
|
||
Rectangle {
|
||
Layout.fillWidth: true
|
||
Layout.preferredHeight: 1
|
||
color: Qt.rgba(Theme.text.r, Theme.text.g, Theme.text.b, 0.12)
|
||
}
|
||
|
||
// Secondary Navigation
|
||
ListView {
|
||
Layout.fillWidth: true
|
||
Layout.preferredHeight: contentHeight
|
||
interactive: false
|
||
|
||
model: ListModel {
|
||
ListElement { viewId: "user"; emoji: "👤"; labelKey: "profile" }
|
||
ListElement { viewId: "docs"; emoji: "📖"; labelKey: "documentation" }
|
||
}
|
||
|
||
delegate: ItemDelegate {
|
||
width: parent.width
|
||
height: 48
|
||
highlighted: currentView === viewId
|
||
|
||
contentItem: RowLayout {
|
||
spacing: 16
|
||
|
||
Text {
|
||
text: emoji
|
||
font.pixelSize: 20
|
||
color: Theme.text
|
||
Layout.leftMargin: 16
|
||
}
|
||
|
||
Text {
|
||
text: LanguageContext.t(labelKey)
|
||
font.pixelSize: 14
|
||
color: Theme.text
|
||
}
|
||
}
|
||
|
||
background: Rectangle {
|
||
color: highlighted ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
||
: (hovered ? Qt.rgba(Theme.text.r, Theme.text.g, Theme.text.b, 0.04) : "transparent")
|
||
}
|
||
|
||
onClicked: navigateTo(viewId)
|
||
}
|
||
}
|
||
|
||
// Divider
|
||
Rectangle {
|
||
Layout.fillWidth: true
|
||
Layout.preferredHeight: 1
|
||
color: Qt.rgba(Theme.text.r, Theme.text.g, Theme.text.b, 0.12)
|
||
}
|
||
|
||
// Settings Section
|
||
Item {
|
||
Layout.fillWidth: true
|
||
Layout.preferredHeight: 48
|
||
|
||
RowLayout {
|
||
anchors.fill: parent
|
||
anchors.leftMargin: 16
|
||
anchors.rightMargin: 16
|
||
|
||
Text {
|
||
text: "💻"
|
||
font.pixelSize: 16
|
||
}
|
||
|
||
Text {
|
||
text: LanguageContext.t("nerdMode")
|
||
font.pixelSize: 14
|
||
color: Theme.text
|
||
Layout.fillWidth: true
|
||
}
|
||
|
||
Switch {
|
||
checked: NerdModeContext.nerdMode
|
||
onToggled: NerdModeContext.setNerdMode(checked)
|
||
}
|
||
}
|
||
}
|
||
|
||
// Theme selector
|
||
ItemDelegate {
|
||
Layout.fillWidth: true
|
||
height: 48
|
||
|
||
contentItem: RowLayout {
|
||
spacing: 16
|
||
|
||
Text {
|
||
text: "🎨"
|
||
font.pixelSize: 16
|
||
Layout.leftMargin: 16
|
||
}
|
||
|
||
Text {
|
||
text: LanguageContext.t("theme") + ": " + Theme.current.name
|
||
font.pixelSize: 14
|
||
color: Theme.text
|
||
}
|
||
}
|
||
|
||
onClicked: themeMenu.open()
|
||
|
||
Menu {
|
||
id: themeMenu
|
||
|
||
Repeater {
|
||
model: Theme.themeKeys
|
||
|
||
MenuItem {
|
||
text: Theme.themes[modelData].name
|
||
checkable: true
|
||
checked: Theme.themeName === modelData
|
||
onTriggered: Theme.setTheme(modelData)
|
||
|
||
indicator: Rectangle {
|
||
implicitWidth: 12
|
||
implicitHeight: 12
|
||
x: 6
|
||
y: parent.height / 2 - 6
|
||
radius: 6
|
||
color: Theme.themes[modelData].primary
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Language selector
|
||
ItemDelegate {
|
||
Layout.fillWidth: true
|
||
height: 48
|
||
|
||
contentItem: RowLayout {
|
||
spacing: 16
|
||
|
||
Text {
|
||
text: "🌐"
|
||
font.pixelSize: 16
|
||
Layout.leftMargin: 16
|
||
}
|
||
|
||
Text {
|
||
text: LanguageContext.t("language") + ": " + LanguageContext.languages[LanguageContext.language].flag
|
||
font.pixelSize: 14
|
||
color: Theme.text
|
||
}
|
||
}
|
||
|
||
onClicked: languageMenu.open()
|
||
|
||
Menu {
|
||
id: languageMenu
|
||
|
||
Repeater {
|
||
model: LanguageContext.languageKeys
|
||
|
||
MenuItem {
|
||
text: LanguageContext.languages[modelData].flag + " " + LanguageContext.languages[modelData].name
|
||
checkable: true
|
||
checked: LanguageContext.language === modelData
|
||
onTriggered: LanguageContext.setLanguage(modelData)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Spacer
|
||
Item { Layout.fillHeight: true }
|
||
|
||
// User chip at bottom - shows connection status
|
||
Rectangle {
|
||
Layout.fillWidth: true
|
||
Layout.preferredHeight: 48
|
||
Layout.margins: 16
|
||
color: Qt.rgba(Theme.text.r, Theme.text.g, Theme.text.b, 0.08)
|
||
radius: 24
|
||
|
||
RowLayout {
|
||
anchors.centerIn: parent
|
||
spacing: 8
|
||
|
||
Text {
|
||
text: "🔌"
|
||
font.pixelSize: 16
|
||
}
|
||
|
||
Text {
|
||
text: "Python API"
|
||
font.pixelSize: 12
|
||
color: Theme.text
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Main Content Area
|
||
Rectangle {
|
||
Layout.fillWidth: true
|
||
Layout.fillHeight: true
|
||
color: Theme.background
|
||
|
||
ColumnLayout {
|
||
anchors.fill: parent
|
||
spacing: 0
|
||
|
||
// App Bar
|
||
Rectangle {
|
||
Layout.fillWidth: true
|
||
Layout.preferredHeight: 64
|
||
color: Theme.paper
|
||
|
||
RowLayout {
|
||
anchors.fill: parent
|
||
anchors.leftMargin: 16
|
||
anchors.rightMargin: 16
|
||
|
||
Text {
|
||
text: {
|
||
switch(currentView) {
|
||
case "tasks": return LanguageContext.t("tasks")
|
||
case "taskDetail": return LanguageContext.t("taskDetail")
|
||
case "newPrompt": return LanguageContext.t("newTask")
|
||
case "user": return LanguageContext.t("profile")
|
||
case "docs": return LanguageContext.t("documentation")
|
||
default: return ""
|
||
}
|
||
}
|
||
font.pixelSize: 20
|
||
font.bold: true
|
||
color: Theme.text
|
||
}
|
||
|
||
Item { Layout.fillWidth: true }
|
||
|
||
// Search button
|
||
CIconButton {
|
||
icon: "🔍"
|
||
tooltip: LanguageContext.t("search") + " (⌘K)"
|
||
onClicked: searchOpen = true
|
||
}
|
||
|
||
// Docs button
|
||
CIconButton {
|
||
icon: "📖"
|
||
tooltip: LanguageContext.t("documentation")
|
||
onClicked: navigateTo("docs")
|
||
}
|
||
|
||
// Refresh button
|
||
CIconButton {
|
||
icon: "🔄"
|
||
tooltip: "Refresh"
|
||
onClicked: app.loadTasks()
|
||
}
|
||
}
|
||
}
|
||
|
||
// Content
|
||
StackLayout {
|
||
Layout.fillWidth: true
|
||
Layout.fillHeight: true
|
||
currentIndex: {
|
||
switch(currentView) {
|
||
case "tasks": return 0
|
||
case "taskDetail": return 1
|
||
case "newPrompt": return 2
|
||
case "user": return 3
|
||
case "docs": return 4
|
||
default: return 0
|
||
}
|
||
}
|
||
|
||
// Tasks View
|
||
TaskList {
|
||
onTaskSelected: function(index) { selectTask(index) }
|
||
}
|
||
|
||
// Task Detail View
|
||
TaskDetail {
|
||
taskIndex: selectedTaskIndex
|
||
onBack: navigateTo("tasks")
|
||
}
|
||
|
||
// New Prompt View
|
||
NewPrompt {
|
||
onSuccess: navigateTo("tasks")
|
||
}
|
||
|
||
// User Info View
|
||
UserInfo {}
|
||
|
||
// Documentation View
|
||
Documentation {}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Search Dialog
|
||
SearchDialog {
|
||
id: searchDialog
|
||
visible: searchOpen
|
||
onRejected: searchOpen = false
|
||
onTaskSelected: function(index) {
|
||
searchOpen = false
|
||
selectTask(index)
|
||
}
|
||
}
|
||
|
||
// Ajax Queue Widget (fixed position, bottom right)
|
||
AjaxQueueWidget {
|
||
anchors.right: parent.right
|
||
anchors.bottom: parent.bottom
|
||
anchors.margins: 16
|
||
}
|
||
}
|