Initial commit of WhisperVoice
This commit is contained in:
905
src/ui/qml/Settings.qml
Normal file
905
src/ui/qml/Settings.qml
Normal file
@@ -0,0 +1,905 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
|
||||
Window {
|
||||
id: root
|
||||
|
||||
width: 850 * (ui ? ui.uiScale : 1.0)
|
||||
height: 620 * (ui ? ui.uiScale : 1.0)
|
||||
|
||||
visible: false
|
||||
flags: Qt.FramelessWindowHint | Qt.Window
|
||||
color: "transparent"
|
||||
title: "Settings"
|
||||
|
||||
// Explicit sizing for Python to read
|
||||
|
||||
// Prevent destruction on close
|
||||
onClosing: (close) => {
|
||||
close.accepted = false
|
||||
root.visible = false
|
||||
}
|
||||
|
||||
// Load Font
|
||||
FontLoader {
|
||||
id: jetBrainsMono
|
||||
source: "fonts/ttf/JetBrainsMono-Bold.ttf"
|
||||
}
|
||||
|
||||
readonly property string mainFont: jetBrainsMono.name
|
||||
property bool isLoaded: false
|
||||
|
||||
Component.onCompleted: {
|
||||
isLoaded = true
|
||||
}
|
||||
|
||||
// --- REUSABLE COMPONENTS ---
|
||||
component StatBox: Rectangle {
|
||||
property string label: ""
|
||||
property string value: ""
|
||||
property string unit: ""
|
||||
property color accent: SettingsStyle.accent
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 80
|
||||
color: "#16161a"
|
||||
radius: 12
|
||||
border.color: SettingsStyle.borderSubtle
|
||||
border.width: 1
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: 4
|
||||
Text { text: label; color: SettingsStyle.textSecondary; font.pixelSize: 11; font.bold: true; anchors.horizontalCenter: parent.horizontalCenter }
|
||||
Row {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 2
|
||||
Text { text: value; color: accent; font.pixelSize: 22; font.bold: true; font.family: "JetBrains Mono" }
|
||||
Text { text: unit; color: SettingsStyle.textSecondary; font.pixelSize: 12; anchors.baseline: parent.bottom; anchors.baselineOffset: -4 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Main Container
|
||||
Rectangle {
|
||||
id: mainContainer
|
||||
anchors.fill: parent
|
||||
radius: 16
|
||||
color: SettingsStyle.background
|
||||
border.color: SettingsStyle.borderSubtle
|
||||
border.width: 1
|
||||
|
||||
opacity: 0
|
||||
transform: Translate {
|
||||
id: entryTranslate
|
||||
y: 20
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
entryAnim.start()
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: entryAnim
|
||||
NumberAnimation { target: mainContainer; property: "opacity"; to: 1; duration: 400; easing.type: Easing.OutCubic }
|
||||
NumberAnimation { target: entryTranslate; property: "y"; to: 0; duration: 500; easing.type: Easing.OutBack; easing.overshoot: 0.8 }
|
||||
}
|
||||
|
||||
// --- TITLE BAR ---
|
||||
Item {
|
||||
id: titleBar
|
||||
height: 60
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onPressed: root.startSystemMove()
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 24
|
||||
anchors.rightMargin: 24
|
||||
|
||||
Image {
|
||||
source: "microphone.svg"
|
||||
sourceSize.width: 18
|
||||
sourceSize.height: 18
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
opacity: 0.7
|
||||
// Colorize the icon
|
||||
layer.enabled: true
|
||||
layer.effect: MultiEffect {
|
||||
colorization: 1.0
|
||||
colorizationColor: SettingsStyle.accent
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "SETTINGS"
|
||||
color: SettingsStyle.textSecondary
|
||||
font.family: mainFont
|
||||
font.pixelSize: 13
|
||||
font.letterSpacing: 2
|
||||
font.bold: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
// Improved Close Button
|
||||
Rectangle {
|
||||
width: 32; height: 32
|
||||
radius: 8
|
||||
color: closeMa.containsMouse ? "#20ff4b4b" : "transparent"
|
||||
border.color: closeMa.containsMouse ? "#40ff4b4b" : "transparent"
|
||||
border.width: 1
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "×"
|
||||
color: closeMa.containsMouse ? "#ff4b4b" : SettingsStyle.textSecondary
|
||||
font.family: mainFont
|
||||
font.pixelSize: 20
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: closeMa
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.close()
|
||||
}
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 150 } }
|
||||
Behavior on border.color { ColorAnimation { duration: 150 } }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: SettingsStyle.borderSubtle
|
||||
}
|
||||
}
|
||||
|
||||
// --- CONTENT AREA ---
|
||||
RowLayout {
|
||||
anchors.top: titleBar.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 0
|
||||
|
||||
// --- SIDEBAR ---
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: 220
|
||||
Layout.minimumWidth: 220
|
||||
Layout.maximumWidth: 220
|
||||
color: Qt.rgba(1, 1, 1, 0.02) // Very subtle separation
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
spacing: 8
|
||||
|
||||
ListModel {
|
||||
id: navModel
|
||||
ListElement { name: "General"; icon: "settings.svg" }
|
||||
ListElement { name: "Audio"; icon: "microphone.svg" }
|
||||
ListElement { name: "Visuals"; icon: "visibility.svg" }
|
||||
ListElement { name: "AI Engine"; icon: "smart_toy.svg" }
|
||||
ListElement { name: "Debug"; icon: "terminal.svg" }
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: navModel
|
||||
delegate: Rectangle {
|
||||
id: navBtnRoot
|
||||
Layout.fillWidth: true
|
||||
height: 38
|
||||
color: stack.currentIndex === index ? SettingsStyle.surfaceHover : (ma.containsMouse ? Qt.rgba(1,1,1,0.03) : "transparent")
|
||||
radius: 6
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 150 } }
|
||||
|
||||
// Left active stripe
|
||||
Rectangle {
|
||||
width: 3
|
||||
height: 20
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
radius: 2
|
||||
color: SettingsStyle.accent
|
||||
visible: stack.currentIndex === index
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 12
|
||||
spacing: 12
|
||||
|
||||
Image {
|
||||
source: icon
|
||||
sourceSize.width: 16
|
||||
sourceSize.height: 16
|
||||
fillMode: Image.PreserveAspectFit
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
opacity: stack.currentIndex === index ? 1.0 : 0.5
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: MultiEffect {
|
||||
colorization: 1.0
|
||||
colorizationColor: stack.currentIndex === index ? SettingsStyle.accent : SettingsStyle.textSecondary
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: name
|
||||
color: stack.currentIndex === index ? SettingsStyle.textPrimary : SettingsStyle.textSecondary
|
||||
font.family: mainFont
|
||||
font.pixelSize: 13
|
||||
font.weight: stack.currentIndex === index ? Font.Bold : Font.Normal
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: ma
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: stack.currentIndex = index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
}
|
||||
|
||||
// Vertical Divider
|
||||
Rectangle {
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
width: 1
|
||||
color: SettingsStyle.borderSubtle
|
||||
}
|
||||
}
|
||||
|
||||
// --- MAIN CONTENT STACK ---
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
color: "transparent"
|
||||
clip: true
|
||||
|
||||
StackLayout {
|
||||
id: stack
|
||||
anchors.fill: parent
|
||||
currentIndex: 0
|
||||
|
||||
// --- TAB: GENERAL ---
|
||||
ScrollView {
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
contentWidth: availableWidth
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 24
|
||||
anchors.margins: 32
|
||||
|
||||
// Header
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 32
|
||||
Layout.rightMargin: 32
|
||||
Text { text: "General"; color: SettingsStyle.textPrimary; font.family: mainFont; font.pixelSize: 24; font.bold: true }
|
||||
Text { text: "System behavior and shortcuts"; color: SettingsStyle.textSecondary; font.family: mainFont; font.pixelSize: 14 }
|
||||
}
|
||||
|
||||
ModernSettingsSection {
|
||||
title: "Application"
|
||||
Layout.margins: 32
|
||||
Layout.topMargin: 0
|
||||
|
||||
content: ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Global Hotkey"
|
||||
description: "Press to record a new shortcut (e.g. Ctrl+Space)"
|
||||
control: ModernKeySequenceRecorder {
|
||||
Layout.preferredWidth: 200
|
||||
currentSequence: ui.getSetting("hotkey")
|
||||
onSequenceChanged: (seq) => ui.setSetting("hotkey", seq)
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Run on Startup"
|
||||
description: "Automatically launch when you log in"
|
||||
control: ModernSwitch {
|
||||
checked: ui.getSetting("run_on_startup")
|
||||
onToggled: ui.setSetting("run_on_startup", checked)
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Input Method"
|
||||
description: "How text is sent to the active window"
|
||||
control: ModernComboBox {
|
||||
width: 160
|
||||
model: ["Clipboard Paste", "Simulate Typing"]
|
||||
currentIndex: model.indexOf(ui.getSetting("input_method"))
|
||||
onActivated: ui.setSetting("input_method", currentText)
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Typing Speed"
|
||||
description: "Chars/min"
|
||||
showSeparator: false
|
||||
control: ModernSlider {
|
||||
Layout.preferredWidth: 200
|
||||
from: 10; to: 6000
|
||||
stepSize: 10
|
||||
snapMode: Slider.SnapAlways
|
||||
value: ui.getSetting("typing_speed")
|
||||
onMoved: ui.setSetting("typing_speed", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- TAB: AUDIO ---
|
||||
ScrollView {
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
contentWidth: availableWidth
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 24
|
||||
anchors.margins: 32
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 32
|
||||
Layout.rightMargin: 32
|
||||
Text { text: "Audio"; color: SettingsStyle.textPrimary; font.family: mainFont; font.pixelSize: 24; font.bold: true }
|
||||
Text { text: "Input devices and signal processing"; color: SettingsStyle.textSecondary; font.family: mainFont; font.pixelSize: 14 }
|
||||
}
|
||||
|
||||
ModernSettingsSection {
|
||||
title: "Devices"
|
||||
Layout.margins: 32
|
||||
Layout.topMargin: 0
|
||||
|
||||
content: ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Microphone"
|
||||
description: "Select your primary input device"
|
||||
control: ModernComboBox {
|
||||
Layout.preferredWidth: 280
|
||||
textRole: "name"
|
||||
valueRole: "id"
|
||||
model: ui.getAudioDevices()
|
||||
onActivated: ui.setSetting("input_device", model[currentIndex].id) // Explicitly use model index
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Save Recordings"
|
||||
description: "Save .wav files to ./recordings folder"
|
||||
showSeparator: false
|
||||
control: ModernSwitch {
|
||||
checked: ui.getSetting("save_recordings")
|
||||
onToggled: ui.setSetting("save_recordings", checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsSection {
|
||||
title: "Signal Processing"
|
||||
Layout.margins: 32
|
||||
Layout.topMargin: 0
|
||||
|
||||
content: ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "VAD Threshold"
|
||||
description: "Silence detection sensitivity (" + (ui.getSetting("silence_threshold") * 100).toFixed(0) + "%)"
|
||||
control: ModernSlider {
|
||||
Layout.preferredWidth: 200
|
||||
from: 1; to: 100
|
||||
value: ui.getSetting("silence_threshold") * 100
|
||||
onMoved: ui.setSetting("silence_threshold", Number((value / 100.0).toFixed(2)))
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Silence Timeout"
|
||||
description: "Stop recording after " + (ui.getSetting("silence_duration")).toFixed(1) + "s of silence"
|
||||
showSeparator: false
|
||||
control: ModernSlider {
|
||||
Layout.preferredWidth: 200
|
||||
from: 0.5; to: 5.0
|
||||
value: ui.getSetting("silence_duration")
|
||||
onMoved: ui.setSetting("silence_duration", Number(value.toFixed(1)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- TAB: VISUALS ---
|
||||
ScrollView {
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
contentWidth: availableWidth
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 24
|
||||
anchors.margins: 32
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 32
|
||||
Layout.rightMargin: 32
|
||||
Text { text: "Visuals"; color: SettingsStyle.textPrimary; font.family: mainFont; font.pixelSize: 24; font.bold: true }
|
||||
Text { text: "Customize the overlay appearance"; color: SettingsStyle.textSecondary; font.family: mainFont; font.pixelSize: 14 }
|
||||
}
|
||||
|
||||
ModernSettingsSection {
|
||||
title: "Overlay"
|
||||
Layout.margins: 32
|
||||
Layout.topMargin: 0
|
||||
|
||||
content: ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "UI Scale"
|
||||
description: "Global interface scaling factor (" + ui.getSetting("ui_scale").toFixed(2) + "x)"
|
||||
control: ModernSlider {
|
||||
Layout.preferredWidth: 200
|
||||
from: 0.75; to: 1.5
|
||||
value: ui.getSetting("ui_scale")
|
||||
onMoved: ui.setSetting("ui_scale", Number(value.toFixed(2)))
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Always on Top"
|
||||
description: "Keep the overlay visible above other windows"
|
||||
control: ModernSwitch {
|
||||
checked: ui.getSetting("always_on_top")
|
||||
onToggled: ui.setSetting("always_on_top", checked)
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Window Opacity"
|
||||
description: "Transparency level"
|
||||
showSeparator: false
|
||||
control: ModernSlider {
|
||||
Layout.preferredWidth: 200
|
||||
from: 0.1; to: 1.0
|
||||
value: ui.getSetting("opacity")
|
||||
onMoved: ui.setSetting("opacity", Number(value.toFixed(2)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsSection {
|
||||
title: "Window Position"
|
||||
Layout.margins: 32
|
||||
Layout.topMargin: 0
|
||||
|
||||
content: ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Anchor Position"
|
||||
description: "Where the overlay snaps to on screen"
|
||||
control: ModernComboBox {
|
||||
width: 160
|
||||
model: ["Bottom Center", "Top Center", "Bottom Right", "Top Right", "Bottom Left", "Top Left"]
|
||||
currentIndex: model.indexOf(ui.getSetting("overlay_position"))
|
||||
onActivated: ui.setSetting("overlay_position", currentText)
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Horizontal Offset"
|
||||
description: "Fine-tune X position (" + ui.getSetting("overlay_offset_x") + "px)"
|
||||
control: ModernSlider {
|
||||
Layout.preferredWidth: 200
|
||||
from: -500; to: 500
|
||||
value: ui.getSetting("overlay_offset_x")
|
||||
onMoved: ui.setSetting("overlay_offset_x", value)
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Vertical Offset"
|
||||
description: "Fine-tune Y position (" + ui.getSetting("overlay_offset_y") + "px)"
|
||||
showSeparator: false
|
||||
control: ModernSlider {
|
||||
Layout.preferredWidth: 200
|
||||
from: -500; to: 500
|
||||
value: ui.getSetting("overlay_offset_y")
|
||||
onMoved: ui.setSetting("overlay_offset_y", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- TAB: AI ENGINE ---
|
||||
ScrollView {
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
contentWidth: availableWidth
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 24
|
||||
anchors.margins: 32
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 32
|
||||
Layout.rightMargin: 32
|
||||
Text { text: "AI Engine"; color: SettingsStyle.textPrimary; font.family: mainFont; font.pixelSize: 24; font.bold: true }
|
||||
Text { text: "Model configuration and performance"; color: SettingsStyle.textSecondary; font.family: mainFont; font.pixelSize: 14 }
|
||||
}
|
||||
|
||||
ModernSettingsSection {
|
||||
title: "Model Config"
|
||||
Layout.margins: 32
|
||||
Layout.topMargin: 0
|
||||
|
||||
content: ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
|
||||
ListModel {
|
||||
id: modelDetailsModel
|
||||
ListElement { name: "tiny"; info: "39M params • <1GB VRAM • 32x speed. Fastest, best for weak hardware." }
|
||||
ListElement { name: "base"; info: "74M params • ~1GB VRAM • 16x speed. Efficient for simple commands." }
|
||||
ListElement { name: "small"; info: "244M params • ~2GB VRAM • 6x speed. Recommended for most users." }
|
||||
ListElement { name: "medium"; info: "769M params • ~5GB VRAM • Accurate, high fidelity. Mid-range GPU req." }
|
||||
ListElement { name: "large-v3"; info: "1.5B params • ~10GB VRAM • Pro quality. Requires high-end hardware." }
|
||||
ListElement { name: "turbo"; info: "800M params • ~6GB VRAM • Large-v3 quality with 8x speed boost." }
|
||||
}
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Model Size"
|
||||
description: "Larger models are smarter but slower"
|
||||
control: ModernComboBox {
|
||||
id: modelSizeCombo
|
||||
width: 140
|
||||
model: ["tiny", "base", "small", "medium", "large-v3", "turbo"]
|
||||
currentIndex: model.indexOf(ui.getSetting("model_size"))
|
||||
onActivated: ui.setSetting("model_size", currentText)
|
||||
}
|
||||
}
|
||||
|
||||
// Model Info Card
|
||||
Rectangle {
|
||||
id: modelInfoCard
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 12
|
||||
Layout.topMargin: 0
|
||||
Layout.bottomMargin: 16
|
||||
height: 54
|
||||
color: "#0a0a0f"
|
||||
radius: 6
|
||||
border.color: SettingsStyle.borderSubtle
|
||||
border.width: 1
|
||||
|
||||
// Improved reactive check
|
||||
property bool isDownloaded: false
|
||||
|
||||
Timer {
|
||||
id: checkTimer
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: parent.checkStatus()
|
||||
}
|
||||
|
||||
function checkStatus() {
|
||||
if (modelSizeCombo && modelSizeCombo.currentText) {
|
||||
isDownloaded = ui.isModelDownloaded(modelSizeCombo.currentText)
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh when notified by Python
|
||||
Connections {
|
||||
target: ui
|
||||
function onModelStatesChanged() {
|
||||
checkStatus()
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: checkStatus()
|
||||
|
||||
// Also check when selection changes
|
||||
Connections {
|
||||
target: modelSizeCombo
|
||||
function onActivated() { modelInfoCard.checkStatus() }
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 12
|
||||
anchors.rightMargin: 12
|
||||
spacing: 12
|
||||
|
||||
Image {
|
||||
source: "smart_toy.svg"
|
||||
sourceSize: Qt.size(16, 16)
|
||||
layer.enabled: true
|
||||
layer.effect: MultiEffect {
|
||||
colorization: 1.0
|
||||
colorizationColor: modelInfoCard.isDownloaded ? SettingsStyle.accent : "#808080"
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
Text {
|
||||
text: {
|
||||
if (ui.isDownloading) return "Downloading AI Core..."
|
||||
for (var i = 0; i < modelDetailsModel.count; i++) {
|
||||
if (modelDetailsModel.get(i).name === modelSizeCombo.currentText) {
|
||||
return modelDetailsModel.get(i).info
|
||||
}
|
||||
}
|
||||
return "Select a model."
|
||||
}
|
||||
color: "#ffffff"
|
||||
font.family: "JetBrains Mono"
|
||||
font.pixelSize: 10
|
||||
opacity: 0.7
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: downloaderBar
|
||||
visible: ui.isDownloading
|
||||
Layout.fillWidth: true
|
||||
height: 2
|
||||
color: "#20ffffff"
|
||||
Rectangle {
|
||||
width: downloaderBar.width * 0.5
|
||||
height: downloaderBar.height
|
||||
color: SettingsStyle.accent
|
||||
SequentialAnimation on x {
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation { from: -width; to: downloaderBar.width; duration: 1500 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: downloadBtn
|
||||
text: "Download"
|
||||
visible: !modelInfoCard.isDownloaded && !ui.isDownloading
|
||||
Layout.preferredHeight: 24
|
||||
Layout.preferredWidth: 80
|
||||
|
||||
contentItem: Text {
|
||||
text: "DOWNLOAD"
|
||||
font.pixelSize: 10; font.bold: true; color: "#000000"; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
background: Rectangle {
|
||||
color: downloadBtn.hovered ? "#ffffff" : SettingsStyle.accent; radius: 4
|
||||
}
|
||||
onClicked: ui.downloadModel(modelSizeCombo.currentText)
|
||||
}
|
||||
|
||||
// Status tag
|
||||
Rectangle {
|
||||
id: statusTag
|
||||
visible: modelInfoCard.isDownloaded && !ui.isDownloading
|
||||
height: 18; width: 64; radius: 4; color: "#1000f2ff"
|
||||
border.color: "#3000f2ff"; border.width: 1
|
||||
Text {
|
||||
anchors.centerIn: statusTag; text: "INSTALLED"; font.pixelSize: 9
|
||||
font.bold: true; color: SettingsStyle.accent; opacity: 0.9
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Language"
|
||||
description: "Force language or Auto-detect"
|
||||
control: ModernComboBox {
|
||||
width: 140
|
||||
model: ["auto", "en", "fr", "de", "es", "it", "ja", "zh", "ru"]
|
||||
currentIndex: model.indexOf(ui.getSetting("language"))
|
||||
onActivated: ui.setSetting("language", currentText)
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Compute Device"
|
||||
description: "Hardware acceleration (CUDA requires NVidia GPU)"
|
||||
control: ModernComboBox {
|
||||
width: 140
|
||||
model: ["auto", "cuda", "cpu"]
|
||||
currentIndex: model.indexOf(ui.getSetting("compute_device"))
|
||||
onActivated: ui.setSetting("compute_device", currentText)
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Precision"
|
||||
description: "Quantization type (int8 is faster, float16 is accurate)"
|
||||
showSeparator: false
|
||||
control: ModernComboBox {
|
||||
width: 140
|
||||
model: ["int8", "float16", "float32"]
|
||||
currentIndex: model.indexOf(ui.getSetting("compute_type"))
|
||||
onActivated: ui.setSetting("compute_type", currentText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsSection {
|
||||
title: "Advanced Decoding"
|
||||
Layout.margins: 32
|
||||
Layout.topMargin: 0
|
||||
|
||||
content: ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Beam Size"
|
||||
description: "Search width (Higher = Better Accuracy, Slower)"
|
||||
control: ModernSlider {
|
||||
Layout.preferredWidth: 200
|
||||
from: 1; to: 10
|
||||
value: ui.getSetting("beam_size")
|
||||
onMoved: ui.setSetting("beam_size", value)
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "VAD Filter"
|
||||
description: "Skip silent audio segments (Speeds up processing)"
|
||||
control: ModernSwitch {
|
||||
checked: ui.getSetting("vad_filter")
|
||||
onToggled: ui.setSetting("vad_filter", checked)
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Hallucination Check"
|
||||
description: "Prevent repetitive text loops (No Repeat N-Gram)"
|
||||
control: ModernSwitch {
|
||||
checked: ui.getSetting("no_repeat_ngram_size") > 0
|
||||
onToggled: ui.setSetting("no_repeat_ngram_size", checked ? 3 : 0)
|
||||
}
|
||||
}
|
||||
|
||||
ModernSettingsItem {
|
||||
label: "Context History"
|
||||
description: "Use previous text to improve coherence"
|
||||
showSeparator: false
|
||||
control: ModernSwitch {
|
||||
checked: ui.getSetting("condition_on_previous_text")
|
||||
onToggled: ui.setSetting("condition_on_previous_text", checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- TAB: DEBUG ---
|
||||
ScrollView {
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
contentWidth: availableWidth
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 16
|
||||
anchors.margins: 32
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 32
|
||||
Layout.rightMargin: 32
|
||||
Text { text: "System Diagnostics"; color: SettingsStyle.textPrimary; font.family: mainFont; font.pixelSize: 24; font.bold: true }
|
||||
Text { text: "Live performance and logs"; color: SettingsStyle.textSecondary; font.family: mainFont; font.pixelSize: 14 }
|
||||
}
|
||||
|
||||
// --- PERFORMANCE STATS ---
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 32
|
||||
Layout.rightMargin: 32
|
||||
spacing: 16
|
||||
|
||||
StatBox { label: "APP CPU"; value: ui.appCpu; unit: "%"; accent: "#00f2ff" }
|
||||
StatBox { label: "APP RAM"; value: ui.appRamMb; unit: "MB"; accent: "#bd93f9" }
|
||||
StatBox { label: "GPU VRAM"; value: ui.appVramMb; unit: "MB"; accent: "#ff79c6" }
|
||||
StatBox { label: "GPU LOAD"; value: ui.appVramPercent; unit: "%"; accent: "#ff5555" }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 300
|
||||
Layout.margins: 32
|
||||
Layout.topMargin: 0
|
||||
color: "#0d0d10"
|
||||
radius: 8
|
||||
border.color: SettingsStyle.borderSubtle
|
||||
border.width: 1
|
||||
clip: true
|
||||
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 12
|
||||
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
|
||||
|
||||
TextArea {
|
||||
id: logArea
|
||||
text: ui.allLogs
|
||||
readOnly: true
|
||||
color: "#cccccc"
|
||||
font.family: "JetBrains Mono"
|
||||
font.pixelSize: 11
|
||||
wrapMode: TextArea.Wrap
|
||||
background: null
|
||||
selectByMouse: true
|
||||
|
||||
Connections {
|
||||
target: ui
|
||||
function onLogAppended(line) {
|
||||
logArea.append(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user