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) } } } } } } }