From 41eb2d8e3e28765a85c1e025a10eb18fb7d711f7 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Thu, 19 Mar 2026 04:04:31 +0000 Subject: [PATCH] =?UTF-8?q?feat(qml):=20MD3=20rework=20batch=203=20?= =?UTF-8?q?=E2=80=94=20atoms=20(CPanel,=20CSection,=20CBlockquote,=20CHigh?= =?UTF-8?q?light,=20CProse,=20CMarkdown)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPanel: radius 16, tonal surface container, tonal primary header CSection: 22px bold title, inline divider CBlockquote: 4px left accent border, tonal primary background, radius 8 CHighlight: tonal color variants at 15% opacity, radius 4 CProse: 16px body, 1.6 line-height, proportional height mode CMarkdown: updated typography to match MD3 body scale Co-Authored-By: Claude Opus 4.6 (1M context) --- qml/components/atoms/CBlockquote.qml | 60 ++++++++------ qml/components/atoms/CHighlight.qml | 41 +++++----- qml/components/atoms/CMarkdown.qml | 20 ++--- qml/components/atoms/CPanel.qml | 115 +++++++++++++++------------ qml/components/atoms/CProse.qml | 34 ++++---- qml/components/atoms/CSection.qml | 50 +++++++----- 6 files changed, 176 insertions(+), 144 deletions(-) diff --git a/qml/components/atoms/CBlockquote.qml b/qml/components/atoms/CBlockquote.qml index a99a65643..e6db813aa 100644 --- a/qml/components/atoms/CBlockquote.qml +++ b/qml/components/atoms/CBlockquote.qml @@ -3,53 +3,65 @@ import QtQuick.Layouts import QmlComponents 1.0 /** - * CBlockquote.qml - Blockquote (mirrors _blockquote.scss) - * Styled quote block with left border + * CBlockquote.qml - Material Design 3 blockquote + * Styled quote block with primary left border and tonal background */ Rectangle { id: root - + property string text: "" property string cite: "" - - color: Theme.mode === "dark" ? Qt.rgba(255, 255, 255, 0.03) : Qt.rgba(0, 0, 0, 0.02) - radius: StyleVariables.radiusSm - + + // MD3 tonal primary background + color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.04) + + // MD3 rounded right side, squared left (border covers left rounding) + radius: 8 + implicitWidth: parent ? parent.width : 400 - implicitHeight: contentCol.implicitHeight + StyleVariables.spacingMd * 2 - - // Left accent border + implicitHeight: contentCol.implicitHeight + 32 + + // Left accent border - 4px primary with radius 2 Rectangle { + id: leftBorder width: 4 - height: parent.height + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left color: Theme.primary radius: 2 } - + ColumnLayout { id: contentCol - anchors.fill: parent - anchors.leftMargin: StyleVariables.spacingMd + 4 - anchors.rightMargin: StyleVariables.spacingMd - anchors.topMargin: StyleVariables.spacingMd - anchors.bottomMargin: StyleVariables.spacingMd - spacing: StyleVariables.spacingSm - + anchors.left: leftBorder.right + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.leftMargin: 16 + anchors.rightMargin: 16 + anchors.topMargin: 16 + anchors.bottomMargin: 16 + spacing: 8 + Text { Layout.fillWidth: true text: root.text - color: Theme.onSurface - font.pixelSize: StyleVariables.fontSizeMd + color: Theme.text + font.pixelSize: 16 font.italic: true + font.letterSpacing: 0.5 wrapMode: Text.Wrap lineHeight: 1.6 + lineHeightMode: Text.ProportionalHeight } - + Text { Layout.fillWidth: true text: "— " + root.cite - color: Theme.onSurfaceVariant - font.pixelSize: StyleVariables.fontSizeSm + color: Theme.textSecondary + font.pixelSize: 14 + font.letterSpacing: 0.25 visible: root.cite !== "" } } diff --git a/qml/components/atoms/CHighlight.qml b/qml/components/atoms/CHighlight.qml index 0496cebb3..9312f1d22 100644 --- a/qml/components/atoms/CHighlight.qml +++ b/qml/components/atoms/CHighlight.qml @@ -2,48 +2,47 @@ import QtQuick import QmlComponents 1.0 /** - * CHighlight.qml - Text highlighting (mirrors _highlight.scss) - * Highlight text with background color + * CHighlight.qml - Material Design 3 inline text highlight + * Highlight text with tonal background color */ Rectangle { id: root - + property alias text: label.text property string variant: "default" // default, success, warning, error, info - - // Color mapping + + // MD3 tonal color mapping readonly property color _bgColor: { switch (variant) { - case "success": return Theme.successContainer - case "warning": return Theme.warningContainer - case "error": return Theme.errorContainer - case "info": return Theme.infoContainer - default: return Theme.mode === "dark" - ? Qt.rgba(255, 255, 0, 0.2) - : Qt.rgba(255, 255, 0, 0.4) + case "success": return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.15) + case "warning": return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.15) + case "error": return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.15) + case "info": return Qt.rgba(Theme.info.r, Theme.info.g, Theme.info.b, 0.15) + default: return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.15) } } - + readonly property color _textColor: { switch (variant) { case "success": return Theme.success case "warning": return Theme.warning case "error": return Theme.error case "info": return Theme.info - default: return Theme.onSurface + default: return Theme.text } } - + color: _bgColor - radius: StyleVariables.radiusSm / 2 - - implicitWidth: label.implicitWidth + StyleVariables.spacingXs * 2 - implicitHeight: label.implicitHeight + 2 - + radius: 4 + + implicitWidth: label.implicitWidth + 8 + implicitHeight: label.implicitHeight + 4 + Text { id: label anchors.centerIn: parent color: root._textColor - font.pixelSize: StyleVariables.fontSizeSm + font.pixelSize: 14 + font.letterSpacing: 0.25 } } diff --git a/qml/components/atoms/CMarkdown.qml b/qml/components/atoms/CMarkdown.qml index 56d4f75b8..bab939d60 100644 --- a/qml/components/atoms/CMarkdown.qml +++ b/qml/components/atoms/CMarkdown.qml @@ -2,25 +2,27 @@ import QtQuick import QmlComponents 1.0 /** - * CMarkdown.qml - Markdown text display (mirrors _markdown.scss) - * Renders markdown-formatted text using Qt's built-in support + * CMarkdown.qml - Material Design 3 markdown text display + * Renders markdown-formatted text with MD3 typography */ Text { id: root - + property string markdown: "" - + text: markdown textFormat: Text.MarkdownText - color: Theme.onSurface - font.pixelSize: StyleVariables.fontSizeMd + color: Theme.text + font.pixelSize: 16 + font.letterSpacing: 0.5 wrapMode: Text.Wrap lineHeight: 1.6 - - // Link styling + lineHeightMode: Text.ProportionalHeight + + // MD3 primary link color linkColor: Theme.primary onLinkActivated: (link) => Qt.openUrlExternally(link) - + // Cursor for links MouseArea { anchors.fill: parent diff --git a/qml/components/atoms/CPanel.qml b/qml/components/atoms/CPanel.qml index 413162f6e..f56226976 100644 --- a/qml/components/atoms/CPanel.qml +++ b/qml/components/atoms/CPanel.qml @@ -4,22 +4,22 @@ import QtQuick.Effects import QmlComponents 1.0 /** - * CPanel.qml - Panel component (mirrors _panel.scss) + * CPanel.qml - Material Design 3 surface container panel * Floating panel with header, body, and footer sections - * + * * Usage: * CPanel { * title: "Queue Status" * icon: "📋" * collapsible: true - * + * * // Content goes in body * Text { text: "Panel content" } * } */ Rectangle { id: root - + // Public properties property string title: "" property string icon: "" @@ -28,57 +28,60 @@ Rectangle { property bool collapsible: false property bool collapsed: false property string footer: "" - + // Content slot default property alias content: bodyContent.data - + // Signals signal headerClicked() - + // Size implicitWidth: 280 implicitHeight: collapsed ? headerRect.height : (headerRect.height + bodyLoader.height + footerLoader.height) - + // Positioning anchors.right: position === "fixed-br" || position === "fixed-tr" ? parent.right : undefined anchors.left: position === "fixed-bl" || position === "fixed-tl" ? parent.left : undefined anchors.bottom: position === "fixed-br" || position === "fixed-bl" ? parent.bottom : undefined anchors.top: position === "fixed-tr" || position === "fixed-tl" ? parent.top : undefined - anchors.margins: position !== "none" ? StyleVariables.spacingMd : 0 - - // Appearance - color: Theme.paper - radius: StyleVariables.radiusMd - - // Shadow for elevated variant + anchors.margins: position !== "none" ? 16 : 0 + + // MD3 Surface container + color: Theme.mode === "dark" ? Qt.rgba(1, 1, 1, 0.08) : Qt.rgba(0.31, 0.31, 0.44, 0.10) + radius: 16 + border.width: 1 + border.color: Theme.mode === "dark" ? Qt.rgba(1, 1, 1, 0.12) : Qt.rgba(0, 0, 0, 0.12) + + // MD3 elevation shadow for elevated variant layer.enabled: variant === "elevated" layer.effect: MultiEffect { shadowEnabled: true - shadowColor: "#40000000" - shadowBlur: 0.4 - shadowVerticalOffset: 6 + shadowColor: "#30000000" + shadowBlur: 0.6 + shadowVerticalOffset: 4 + shadowHorizontalOffset: 0 } - + // Smooth collapse animation Behavior on implicitHeight { - NumberAnimation { duration: StyleVariables.transitionNormal; easing.type: Easing.OutCubic } + NumberAnimation { duration: 250; easing.type: Easing.OutCubic } } - + // Layout ColumnLayout { anchors.fill: parent spacing: 0 - + // Header Rectangle { id: headerRect Layout.fillWidth: true - Layout.preferredHeight: 40 - color: Qt.darker(Theme.primary, 1.2) + Layout.preferredHeight: 48 + color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) radius: root.radius visible: root.title || root.icon - - // Square off bottom corners + + // Square off bottom corners when not collapsed Rectangle { anchors.left: parent.left anchors.right: parent.right @@ -87,7 +90,7 @@ Rectangle { color: parent.color visible: !root.collapsed } - + MouseArea { anchors.fill: parent cursorShape: root.collapsible ? Qt.PointingHandCursor : Qt.ArrowCursor @@ -98,98 +101,104 @@ Rectangle { root.headerClicked() } } - + RowLayout { anchors.fill: parent - anchors.leftMargin: StyleVariables.spacingMd - anchors.rightMargin: StyleVariables.spacingMd - spacing: StyleVariables.spacingSm - + anchors.leftMargin: 16 + anchors.rightMargin: 16 + spacing: 12 + // Icon Text { visible: root.icon text: root.icon - font.pixelSize: StyleVariables.fontSizeMd - color: "#ffffff" + font.pixelSize: 16 + color: Theme.primary } - + // Title Text { Layout.fillWidth: true text: root.title - font.pixelSize: StyleVariables.fontSizeSm + font.pixelSize: 14 font.weight: Font.Medium - color: "#ffffff" + font.letterSpacing: 0.1 + color: Theme.text elide: Text.ElideRight } - + // Collapse indicator Text { visible: root.collapsible text: root.collapsed ? "▼" : "▲" font.pixelSize: 10 - color: "#cccccc" + color: Theme.textSecondary } } } - + // Body Loader { id: bodyLoader Layout.fillWidth: true active: !root.collapsed visible: active - + sourceComponent: Rectangle { width: bodyLoader.width - height: Math.min(bodyContent.implicitHeight, 300) + height: Math.min(bodyContent.implicitHeight + 32, 332) color: "transparent" clip: true - + Flickable { anchors.fill: parent + anchors.topMargin: 16 + anchors.bottomMargin: 16 + anchors.leftMargin: 16 + anchors.rightMargin: 16 contentHeight: bodyContent.implicitHeight clip: true boundsBehavior: Flickable.StopAtBounds - + ColumnLayout { id: bodyContent width: parent.width - spacing: StyleVariables.spacingSm + spacing: 8 } - + ScrollBar.vertical: ScrollBar { policy: bodyContent.implicitHeight > 300 ? ScrollBar.AsNeeded : ScrollBar.AlwaysOff } } } } - + // Footer Loader { id: footerLoader Layout.fillWidth: true active: root.footer && !root.collapsed visible: active - + sourceComponent: Rectangle { width: footerLoader.width - height: 32 + height: 40 color: "transparent" - + // Top border Rectangle { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right height: 1 - color: Theme.divider + color: Theme.mode === "dark" ? Qt.rgba(1, 1, 1, 0.12) : Qt.rgba(0, 0, 0, 0.12) } - + Text { anchors.centerIn: parent text: root.footer - font.pixelSize: StyleVariables.fontSizeXs + font.pixelSize: 12 + font.letterSpacing: 0.4 color: Theme.textSecondary } } diff --git a/qml/components/atoms/CProse.qml b/qml/components/atoms/CProse.qml index 6bdcee259..e54e3595c 100644 --- a/qml/components/atoms/CProse.qml +++ b/qml/components/atoms/CProse.qml @@ -3,19 +3,19 @@ import QtQuick.Layouts import QmlComponents 1.0 /** - * CProse.qml - Long-form text container (mirrors _prose.scss) - * Optimized typography for reading + * CProse.qml - Material Design 3 long-form text container + * Rich text container with proper line-height and typography */ ColumnLayout { id: root - + property string size: "md" // sm, md, lg property real maxWidth: 680 // Optimal reading width - + default property alias content: contentItem.data - - spacing: StyleVariables.spacingMd - + + spacing: 16 + // Content container with max-width Item { id: contentItem @@ -23,8 +23,8 @@ ColumnLayout { Layout.maximumWidth: root.maxWidth Layout.alignment: Qt.AlignHCenter implicitHeight: childrenRect.height - - // Apply prose styles to child Text elements + + // Apply MD3 prose styles to child Text elements Component.onCompleted: { for (var i = 0; i < children.length; i++) { var child = children[i] @@ -34,21 +34,23 @@ ColumnLayout { } } } - + function applyProseStyle(textItem) { - textItem.color = Theme.onSurface + textItem.color = Theme.text textItem.wrapMode = Text.Wrap - textItem.lineHeight = 1.7 - + textItem.lineHeight = 1.6 + textItem.lineHeightMode = Text.ProportionalHeight + textItem.font.letterSpacing = 0.5 + switch (size) { case "sm": - textItem.font.pixelSize = StyleVariables.fontSizeSm + textItem.font.pixelSize = 14 break case "lg": - textItem.font.pixelSize = StyleVariables.fontSizeLg + textItem.font.pixelSize = 18 break default: - textItem.font.pixelSize = StyleVariables.fontSizeMd + textItem.font.pixelSize = 16 } } } diff --git a/qml/components/atoms/CSection.qml b/qml/components/atoms/CSection.qml index baa4c2596..0c4798f44 100644 --- a/qml/components/atoms/CSection.qml +++ b/qml/components/atoms/CSection.qml @@ -3,58 +3,66 @@ import QtQuick.Layouts import QmlComponents 1.0 /** - * CSection.qml - Content section (mirrors _section.scss) - * Groups related content with optional title + * CSection.qml - Material Design 3 content section + * Groups related content with optional title and subtitle */ ColumnLayout { id: root - + property string title: "" property string subtitle: "" property bool divider: false property string spacing: "md" // sm, md, lg - + default property alias content: contentItem.data - + spacing: { switch (root.spacing) { - case "sm": return StyleVariables.spacingSm - case "lg": return StyleVariables.spacingLg - default: return StyleVariables.spacingMd + case "sm": return 8 + case "lg": return 24 + default: return 16 } } - + // Header ColumnLayout { Layout.fillWidth: true - spacing: StyleVariables.spacingXs + spacing: 4 visible: root.title !== "" || root.subtitle !== "" - + Text { Layout.fillWidth: true text: root.title - color: Theme.onSurface - font.pixelSize: StyleVariables.fontSizeLg - font.weight: Font.DemiBold + color: Theme.text + font.pixelSize: 22 + font.weight: Font.Bold + font.letterSpacing: 0 + lineHeight: 1.27 + lineHeightMode: Text.ProportionalHeight visible: root.title !== "" } - + Text { Layout.fillWidth: true text: root.subtitle - color: Theme.onSurfaceVariant - font.pixelSize: StyleVariables.fontSizeSm + color: Theme.textSecondary + font.pixelSize: 14 + font.letterSpacing: 0.25 + lineHeight: 1.43 + lineHeightMode: Text.ProportionalHeight wrapMode: Text.Wrap visible: root.subtitle !== "" } } - - // Divider after header - CDivider { + + // Bottom divider after header + Rectangle { Layout.fillWidth: true + height: 1 + color: Theme.mode === "dark" ? Qt.rgba(1, 1, 1, 0.12) : Qt.rgba(0, 0, 0, 0.12) visible: root.divider && root.title !== "" } - + // Content Item { id: contentItem