feat: rebuild the interface after Haruna

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
triandClaude Opus 5.5 committed 2026-10-28 09:26:47 +07:00
1 parent 54ad4eecf3
commit 614a31db1b
45 files changed
+3818 -798

No files matched your search

+175
View File
@@ -0,0 +1,175 @@
// SPDX-FileCopyrightText: 2026 Tri Vo Khac
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import org.kde.ki18n
import org.kde.kirigami as Kirigami
SettingsBasePage {
id: root
required property TranslationService translator
ColumnLayout {
Kirigami.InlineMessage {
Layout.fillWidth: true
type: Kirigami.MessageType.Error
text: root.translator.errorText || ApiKeyStore.error
visible: text.length > 0
showCloseButton: true
onVisibleChanged: if (!visible) root.translator.clearError()
}
GridLayout {
columns: 2
Label {
text: KI18n.i18nc("@label:textbox", "API base URL")
Layout.alignment: Qt.AlignRight
}
TextField {
placeholderText: "https://example.com/v1"
text: QtPotatoSettings.apiBaseUrl
inputMethodHints: Qt.ImhUrlCharactersOnly | Qt.ImhNoAutoUppercase
Layout.fillWidth: true
onTextEdited: QtPotatoSettings.apiBaseUrl = text.trim()
onEditingFinished: QtPotatoSettings.save()
}
Label {
text: KI18n.i18nc("@label:textbox", "API key")
Layout.alignment: Qt.AlignRight
}
RowLayout {
Kirigami.PasswordField {
id: apiKeyField
placeholderText: ApiKeyStore.hasKey
? KI18n.i18nc("@info:placeholder", "Saved in the wallet. Type a new key to replace it.")
: KI18n.i18nc("@info:placeholder", "Not set")
Layout.fillWidth: true
onEditingFinished: {
if (text.trim().length > 0) {
ApiKeyStore.setKey(text)
text = ""
}
}
}
Button {
visible: ApiKeyStore.hasKey
icon.name: "edit-delete-remove"
text: KI18n.i18nc("@action:button", "Remove")
onClicked: ApiKeyStore.setKey("")
ToolTip.text: KI18n.i18nc("@info:tooltip", "Remove the saved API key from the wallet")
ToolTip.visible: hovered
ToolTip.delay: Kirigami.Units.toolTipDelay
}
}
Label {
text: KI18n.i18nc("@label:listbox", "Model")
Layout.alignment: Qt.AlignRight
}
RowLayout {
ComboBox {
id: modelCombo
// Swapping the list makes the combo box jump to its first entry; that
// must not overwrite the model the user chose.
property bool updatingList: false
function showModels() {
const chosen = QtPotatoSettings.model
updatingList = true
model = root.translator.availableModels
currentIndex = root.translator.availableModels.indexOf(chosen)
editText = chosen
updatingList = false
}
editable: true
Layout.fillWidth: true
onEditTextChanged: if (!updatingList) QtPotatoSettings.model = editText.trim()
onActivated: QtPotatoSettings.save()
Component.onCompleted: showModels()
Connections {
target: root.translator
function onAvailableModelsChanged() {
modelCombo.showModels()
}
}
}
Button {
icon.name: "view-refresh"
text: root.translator.fetchingModels
? KI18n.i18nc("@action:button", "Loading…")
: KI18n.i18nc("@action:button", "Load Models")
enabled: !root.translator.fetchingModels
onClicked: root.translator.fetchModels()
ToolTip.text: KI18n.i18nc("@info:tooltip", "Ask the API which models it offers")
ToolTip.visible: hovered
ToolTip.delay: Kirigami.Units.toolTipDelay
}
}
Item {
Layout.preferredWidth: 1
visible: modelCountLabel.visible
}
Label {
id: modelCountLabel
visible: root.translator.availableModels.length > 0
text: KI18n.i18ncp("@info", "%1 model available", "%1 models available", root.translator.availableModels.length)
opacity: 0.7
}
Label {
text: KI18n.i18nc("@label:textbox", "Translate into")
Layout.alignment: Qt.AlignRight
}
TextField {
text: QtPotatoSettings.targetLanguage
Layout.fillWidth: true
onTextEdited: QtPotatoSettings.targetLanguage = text.trim()
onEditingFinished: QtPotatoSettings.save()
}
Label {
text: KI18n.i18nc("@label:spinbox", "Lines per request")
Layout.alignment: Qt.AlignRight
}
SpinBox {
from: 5
to: 200
stepSize: 5
value: QtPotatoSettings.batchSize
onValueModified: {
QtPotatoSettings.batchSize = value
QtPotatoSettings.save()
}
}
Item { Layout.preferredWidth: 1 }
Label {
text: KI18n.i18nc("@info", "Works with any OpenAI-compatible API. The whole subtitle track is translated in batches before it is shown, and the result is kept so the same track is never translated twice.")
wrapMode: Text.WordWrap
opacity: 0.7
Layout.fillWidth: true
Layout.topMargin: Kirigami.Units.largeSpacing
}
}
}
}