Split the bar panel into one QML file per stage
Panel.qml keeps the state, the CLI calls and the stage switch (889 lines, was 2360); each stage is its own file taking the panel as a required property. The omarchy installer copies every QML/JS file and clears stale ones. tests/static.sh filters qmllint on [syntax], not the word error. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,276 @@
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Ui
|
||||
import "Modes.js" as Modes
|
||||
|
||||
// ModeList.qml - the modes to switch to, each unfolding its presets.
|
||||
//
|
||||
// One stage of the claude-mode panel. Panel.qml hands itself in as `panel`
|
||||
// and owns all the state and actions; this file only draws them.
|
||||
Column {
|
||||
id: stage
|
||||
required property var panel
|
||||
|
||||
width: parent.width
|
||||
visible: stage.panel.stage === "list"
|
||||
spacing: Style.space(10)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "SWITCH TO"
|
||||
visible: stage.panel.stage === "list"
|
||||
foreground: Color.popups.text
|
||||
fontFamily: stage.panel.uiFont
|
||||
}
|
||||
|
||||
// ---- The modes, current one marked rather than hidden. The CLI omits
|
||||
// the active mode because a list you arrow through should not offer a
|
||||
// no-op; here the list is also the status display, so it stays.
|
||||
Column {
|
||||
width: parent.width
|
||||
visible: stage.panel.stage === "list"
|
||||
spacing: Style.space(1)
|
||||
|
||||
Repeater {
|
||||
model: stage.panel.stage === "list" ? stage.panel.modeOrder : []
|
||||
|
||||
Column {
|
||||
id: modeEntry
|
||||
required property var modelData
|
||||
width: stage.width
|
||||
|
||||
readonly property string thisMode: String(modelData)
|
||||
readonly property bool isCurrent: thisMode === stage.panel.mode
|
||||
readonly property bool isExpanded: stage.panel.expandedMode === thisMode
|
||||
readonly property var presetList: Modes.presetsFor(stage.panel.health, thisMode, stage.panel.defaultPresets)
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: Style.space(30)
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: -Style.space(6)
|
||||
anchors.rightMargin: -Style.space(6)
|
||||
radius: Style.space(4)
|
||||
color: modeEntry.isCurrent
|
||||
? Qt.rgba(Color.accent.r, Color.accent.g, Color.accent.b, 0.12)
|
||||
: (modeHover.containsMouse
|
||||
? Qt.rgba(Color.popups.text.r, Color.popups.text.g, Color.popups.text.b, 0.07)
|
||||
: "transparent")
|
||||
}
|
||||
|
||||
BrandIcon {
|
||||
id: modeGlyph
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.space(3)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
pathData: stage.panel.pLogo(modeEntry.thisMode)
|
||||
opticalScale: stage.panel.pLogoScale(modeEntry.thisMode)
|
||||
color: modeEntry.isCurrent ? Color.accent : Color.popups.text
|
||||
iconSize: Style.space(16)
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.left: modeGlyph.right
|
||||
anchors.leftMargin: Style.space(8)
|
||||
anchors.right: modeMark.left
|
||||
anchors.rightMargin: Style.space(6)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 0
|
||||
|
||||
Text {
|
||||
text: stage.panel.pTitle(modeEntry.thisMode)
|
||||
color: Color.popups.text
|
||||
font.family: stage.panel.uiFont
|
||||
font.pixelSize: Style.font.body
|
||||
font.bold: modeEntry.isCurrent
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
text: stage.panel.pBlurb(modeEntry.thisMode)
|
||||
color: Color.muted
|
||||
elide: Text.ElideRight
|
||||
font.family: stage.panel.uiFont
|
||||
font.pixelSize: Style.space(10)
|
||||
}
|
||||
}
|
||||
|
||||
// Checkmark on the active mode; a chevron on a gateway row that
|
||||
// has a preset list waiting behind it.
|
||||
Text {
|
||||
id: modeMark
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: modeEntry.isCurrent
|
||||
? String.fromCodePoint(0xF012C)
|
||||
: (Modes.needsPreset(modeEntry.thisMode)
|
||||
? String.fromCodePoint(modeEntry.isExpanded ? 0xF0140 : 0xF0142)
|
||||
: "")
|
||||
color: modeEntry.isCurrent ? Color.accent : Color.muted
|
||||
font.family: stage.panel.uiFont
|
||||
font.pixelSize: Style.font.body
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: modeHover
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: !stage.panel.busy
|
||||
onClicked: stage.panel.activate(modeEntry.thisMode)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Presets for this provider, unfolded in place. Shown even
|
||||
// with none left, so "New preset…" is still there to click.
|
||||
Column {
|
||||
width: parent.width
|
||||
visible: modeEntry.isExpanded
|
||||
spacing: Style.space(1)
|
||||
|
||||
Repeater {
|
||||
model: modeEntry.isExpanded ? modeEntry.presetList : []
|
||||
|
||||
Item {
|
||||
id: presetRow
|
||||
required property var modelData
|
||||
width: modeEntry.width
|
||||
height: Style.space(26)
|
||||
|
||||
readonly property bool isActive: modeEntry.isCurrent && String(modelData.name) === stage.panel.preset
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Style.space(20)
|
||||
anchors.rightMargin: -Style.space(6)
|
||||
radius: Style.space(4)
|
||||
color: presetHover.containsMouse
|
||||
? Qt.rgba(Color.accent.r, Color.accent.g, Color.accent.b, 0.14)
|
||||
: "transparent"
|
||||
}
|
||||
|
||||
Text {
|
||||
id: presetName
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.space(30)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: presetRow.modelData.name
|
||||
color: presetRow.isActive ? Color.accent : Color.popups.text
|
||||
font.family: stage.panel.uiFont
|
||||
font.pixelSize: Style.space(12)
|
||||
font.bold: presetRow.isActive
|
||||
}
|
||||
|
||||
// Only worth saying when there is a choice to tell apart.
|
||||
Text {
|
||||
id: defaultTag
|
||||
visible: modeEntry.presetList.length > 1
|
||||
&& String(presetRow.modelData.name) === stage.panel.defaultPresets[modeEntry.thisMode]
|
||||
anchors.left: presetName.right
|
||||
anchors.leftMargin: Style.space(6)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "default"
|
||||
color: Color.muted
|
||||
font.family: stage.panel.uiFont
|
||||
font.pixelSize: Style.space(9)
|
||||
}
|
||||
|
||||
// The opus mapping is the one that tells you what you are
|
||||
// about to be talking to; the rest is in the tooltip.
|
||||
Text {
|
||||
anchors.left: defaultTag.visible ? defaultTag.right : presetName.right
|
||||
anchors.leftMargin: Style.space(8)
|
||||
anchors.right: gearButton.visible ? gearButton.left : parent.right
|
||||
anchors.rightMargin: Style.space(4)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: presetRow.modelData.models && presetRow.modelData.models.opus
|
||||
? String(presetRow.modelData.models.opus)
|
||||
: ""
|
||||
color: Color.muted
|
||||
elide: Text.ElideLeft
|
||||
font.family: stage.panel.uiFont
|
||||
font.pixelSize: Style.space(10)
|
||||
}
|
||||
|
||||
// Clicking the row switches; the gear opens that preset in the
|
||||
// editor instead. Per row rather than per provider, because
|
||||
// two presets of one provider can map tiers differently - and
|
||||
// two LM Studio presets can sit on two different machines.
|
||||
MouseArea {
|
||||
id: presetHover
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: gearButton.visible ? gearButton.width : 0
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: !stage.panel.busy
|
||||
onClicked: stage.panel.switchTo(modeEntry.thisMode, String(presetRow.modelData.name))
|
||||
}
|
||||
|
||||
Item {
|
||||
id: gearButton
|
||||
visible: true
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: Style.space(22)
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: String.fromCodePoint(0xF0493)
|
||||
color: gearArea.containsMouse ? Color.accent : Color.muted
|
||||
font.family: stage.panel.uiFont
|
||||
font.pixelSize: Style.space(12)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: gearArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: !stage.panel.busy
|
||||
onClicked: stage.panel.openPresetEditor(String(presetRow.modelData.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: modeEntry.width
|
||||
height: Style.space(24)
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Style.space(20)
|
||||
anchors.rightMargin: -Style.space(6)
|
||||
radius: Style.space(4)
|
||||
color: newPresetArea.containsMouse
|
||||
? Qt.rgba(Color.accent.r, Color.accent.g, Color.accent.b, 0.14)
|
||||
: "transparent"
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.space(30)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "New " + stage.panel.pTitle(modeEntry.thisMode) + " preset…"
|
||||
color: newPresetArea.containsMouse ? Color.accent : Color.muted
|
||||
font.family: stage.panel.uiFont
|
||||
font.pixelSize: Style.space(11)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: newPresetArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: !stage.panel.busy
|
||||
onClicked: stage.panel.openNewPreset(modeEntry.thisMode, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user