This commit is contained in:
timka123 committed 2026-10-18 18:02:05 +03:00
commit 17a0a3c4c9
121 files changed
+17641

No files matched your search

@@ -0,0 +1,391 @@
// Settings, split by where they live.
//
// Anything under "This computer" is local to this machine (and saved in
// ~/.config/kadence/desktop.json). Everything else is server-side, shared with
// the web app and the Telegram bot.
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
import "components"
ScrollView {
id: page
contentWidth: availableWidth
clip: true
readonly property var pomodoro: controller.settings.pomodoro ? controller.settings.pomodoro : null
readonly property var telegram: controller.settings.notifications
? controller.settings.notifications.telegram : null
readonly property var report: controller.settings.notifications
? controller.settings.notifications.report : null
function patchPomodoro(key, value) {
var patch = {}; patch[key] = value
controller.patchSettings({ pomodoro: patch })
}
function patchTelegram(key, value) {
var patch = {}; patch[key] = value
controller.patchSettings({ notifications: { telegram: patch } })
}
ColumnLayout {
width: page.availableWidth
spacing: 16
ColumnLayout {
Layout.fillWidth: true
Layout.margins: 20
spacing: 16
// ---- connection --------------------------------------------------
Card {
title: "Backend"
subtitle: controller.connected ? "Connected to " + controller.config.server_url
: controller.connectionError
RowLayout {
Layout.fillWidth: true
TextField {
id: serverField
Layout.fillWidth: true
text: controller.config.server_url
placeholderText: "http://localhost:8000"
}
TextField {
id: tokenField
Layout.preferredWidth: 180
text: controller.config.api_token
placeholderText: "API token (optional)"
echoMode: TextInput.Password
}
PrimaryButton {
text: "Connect"
onClicked: {
controller.setConfig("server_url", serverField.text.trim())
controller.setConfig("api_token", tokenField.text)
}
}
}
RowLayout {
Label { text: "Poll every"; font.pixelSize: 12 }
SpinBox {
from: 1; to: 60
value: controller.config.poll_seconds
onValueModified: controller.setConfig("poll_seconds", value)
}
Label { text: "seconds"; font.pixelSize: 12 }
Item { Layout.fillWidth: true }
Button { text: "Open the web app"; flat: true; onClicked: controller.openWebUi() }
}
}
// ---- timer -------------------------------------------------------
Card {
title: "Timer"
subtitle: "Shared with the web app and the Telegram bot."
GridLayout {
Layout.fillWidth: true
columns: page.width > 700 ? 4 : 2
columnSpacing: 16
rowSpacing: 8
ColumnLayout {
Label { text: "Focus (min)"; font.pixelSize: 11; opacity: 0.7 }
SpinBox {
from: 1; to: 240
value: page.pomodoro ? page.pomodoro.work_minutes : 25
onValueModified: page.patchPomodoro("work_minutes", value)
}
}
ColumnLayout {
Label { text: "Short break"; font.pixelSize: 11; opacity: 0.7 }
SpinBox {
from: 1; to: 120
value: page.pomodoro ? page.pomodoro.short_break_minutes : 5
onValueModified: page.patchPomodoro("short_break_minutes", value)
}
}
ColumnLayout {
Label { text: "Long break"; font.pixelSize: 11; opacity: 0.7 }
SpinBox {
from: 1; to: 240
value: page.pomodoro ? page.pomodoro.long_break_minutes : 15
onValueModified: page.patchPomodoro("long_break_minutes", value)
}
}
ColumnLayout {
Label { text: "Long break every"; font.pixelSize: 11; opacity: 0.7 }
SpinBox {
from: 1; to: 12
value: page.pomodoro ? page.pomodoro.cycles_before_long_break : 4
onValueModified: page.patchPomodoro("cycles_before_long_break", value)
}
}
ColumnLayout {
Label { text: "Daily goal"; font.pixelSize: 11; opacity: 0.7 }
SpinBox {
from: 1; to: 40
value: page.pomodoro ? page.pomodoro.daily_goal_pomodoros : 8
onValueModified: page.patchPomodoro("daily_goal_pomodoros", value)
}
}
}
Flow {
Layout.fillWidth: true
spacing: 16
Switch {
text: "Auto-start breaks"
checked: page.pomodoro ? page.pomodoro.auto_start_breaks : true
onToggled: page.patchPomodoro("auto_start_breaks", checked)
}
Switch {
text: "Auto-start next focus"
checked: page.pomodoro ? page.pomodoro.auto_start_work : false
onToggled: page.patchPomodoro("auto_start_work", checked)
}
Switch {
text: "Allow pausing"
checked: page.pomodoro ? page.pomodoro.allow_pause : true
onToggled: page.patchPomodoro("allow_pause", checked)
}
Switch {
text: "Strict mode"
checked: page.pomodoro ? page.pomodoro.strict_mode : false
onToggled: page.patchPomodoro("strict_mode", checked)
ToolTip.visible: hovered
ToolTip.text: "Abandoning a focus interval requires a reason."
}
}
}
// ---- desktop integration ----------------------------------------
Card {
title: "This computer"
subtitle: "Native desktop features. Stored locally, not on the server."
Flow {
Layout.fillWidth: true
spacing: 16
Switch {
text: "Tray icon"
checked: controller.config.tray_icon
onToggled: controller.setConfig("tray_icon", checked)
}
Switch {
text: "Close to tray"
checked: controller.config.close_to_tray
onToggled: controller.setConfig("close_to_tray", checked)
}
Switch {
text: "Notifications"
checked: controller.config.notifications
onToggled: controller.setConfig("notifications", checked)
}
Switch {
text: "Notification buttons"
checked: controller.config.notification_actions
onToggled: controller.setConfig("notification_actions", checked)
ToolTip.visible: hovered
ToolTip.text: "“Start break” / “Skip” directly on the notification."
}
Switch {
text: "Keep the screen awake"
checked: controller.config.inhibit_screensaver
onToggled: controller.setConfig("inhibit_screensaver", checked)
}
Switch {
text: "Do Not Disturb while focusing"
checked: controller.config.do_not_disturb
onToggled: controller.setConfig("do_not_disturb", checked)
}
Switch {
text: "Pause music when focus starts"
checked: controller.config.pause_media
onToggled: controller.setConfig("pause_media", checked)
ToolTip.visible: hovered
ToolTip.text: "Any MPRIS player: Spotify, VLC, mpv, browsers."
}
Switch {
text: "Taskbar progress"
checked: controller.config.taskbar_progress
onToggled: controller.setConfig("taskbar_progress", checked)
}
Switch {
text: "Pause when the screen locks"
checked: controller.config.pause_on_lock
onToggled: controller.setConfig("pause_on_lock", checked)
}
Switch {
text: "D-Bus control"
checked: controller.config.dbus_service
onToggled: controller.setConfig("dbus_service", checked)
ToolTip.visible: hovered
ToolTip.text: "org.kadence.Desktop — bind it to a global shortcut."
}
}
RowLayout {
Layout.fillWidth: true
Label { text: "Pause after"; font.pixelSize: 12 }
SpinBox {
from: 0; to: 120
value: controller.config.pause_on_idle_minutes
onValueModified: controller.setConfig("pause_on_idle_minutes", value)
}
Label { text: "idle minutes (0 = never)"; font.pixelSize: 12 }
Item { Layout.fillWidth: true }
}
RowLayout {
Layout.fillWidth: true
Switch {
text: "Start with the session"
checked: controller.config.autostart
onToggled: controller.setConfig("autostart", checked)
}
Switch {
text: "systemd user service"
checked: controller.config.service_enabled
onToggled: controller.setConfig("service_enabled", checked)
ToolTip.visible: hovered
ToolTip.text: "Restarts the app if it dies; logs to the journal."
}
Switch {
text: "Start minimised"
checked: controller.config.start_minimized
onToggled: controller.setConfig("start_minimized", checked)
}
}
}
// ---- telegram ----------------------------------------------------
Card {
title: "Telegram"
subtitle: "Daily report, morning briefing and the nudges that get you back to work."
RowLayout {
Layout.fillWidth: true
Switch {
text: "Enabled"
checked: page.telegram ? page.telegram.enabled : false
onToggled: page.patchTelegram("enabled", checked)
}
TextField {
Layout.fillWidth: true
placeholderText: "Bot token from @BotFather"
text: page.telegram ? page.telegram.bot_token : ""
onEditingFinished: if (text !== "••••••••") page.patchTelegram("bot_token", text)
}
TextField {
Layout.preferredWidth: 160
placeholderText: "Chat id"
text: page.telegram ? page.telegram.chat_id : ""
onEditingFinished: page.patchTelegram("chat_id", text)
}
}
Flow {
Layout.fillWidth: true
spacing: 16
Switch {
text: "Daily report"
checked: page.telegram ? page.telegram.daily_report : true
onToggled: page.patchTelegram("daily_report", checked)
}
Switch {
text: "Morning briefing"
checked: page.telegram ? page.telegram.morning_briefing : true
onToggled: page.patchTelegram("morning_briefing", checked)
}
Switch {
text: "Weekly review"
checked: page.telegram ? page.telegram.weekly_report : true
onToggled: page.patchTelegram("weekly_report", checked)
}
Switch {
text: "Nudge me when idle"
checked: page.telegram ? page.telegram.nudge_enabled : true
onToggled: page.patchTelegram("nudge_enabled", checked)
}
Switch {
text: "Chat commands"
checked: page.telegram ? page.telegram.commands_enabled : true
onToggled: page.patchTelegram("commands_enabled", checked)
}
}
RowLayout {
Layout.fillWidth: true
ColumnLayout {
Label { text: "Report at (HH:MM)"; font.pixelSize: 11; opacity: 0.7 }
TextField {
text: page.telegram ? page.telegram.daily_report_time : "21:00"
onEditingFinished: page.patchTelegram("daily_report_time", text)
}
}
ColumnLayout {
Label { text: "Briefing at (HH:MM)"; font.pixelSize: 11; opacity: 0.7 }
TextField {
text: page.telegram ? page.telegram.morning_briefing_time : "09:00"
onEditingFinished: page.patchTelegram("morning_briefing_time", text)
}
}
ColumnLayout {
Label { text: "Nudge after (min idle)"; font.pixelSize: 11; opacity: 0.7 }
SpinBox {
from: 10; to: 600
value: page.telegram ? page.telegram.nudge_after_idle_minutes : 90
onValueModified: page.patchTelegram("nudge_after_idle_minutes", value)
}
}
ColumnLayout {
Label { text: "Tone"; font.pixelSize: 11; opacity: 0.7 }
ComboBox {
model: ["friendly", "coach", "drill_sergeant", "dry"]
currentIndex: page.report ? model.indexOf(page.report.tone) : 0
onActivated: function (index) {
controller.patchSettings({ notifications: { report: { tone: model[index] } } })
}
}
}
ColumnLayout {
Label { text: "Language"; font.pixelSize: 11; opacity: 0.7 }
ComboBox {
model: ["en", "ru"]
currentIndex: page.report && page.report.language === "ru" ? 1 : 0
onActivated: function (index) {
controller.patchSettings({ notifications: { report: { language: model[index] } } })
}
}
}
}
}
Card {
title: "Shortcuts and scripting"
subtitle: "The app exposes org.kadence.Desktop on the session bus."
Label {
Layout.fillWidth: true
wrapMode: Text.WordWrap
font.family: "Roboto Mono, monospace"
font.pixelSize: 11
text: "qdbus org.kadence.Desktop /Timer org.kadence.Timer.Toggle\n"
+ "qdbus org.kadence.Desktop /Timer org.kadence.Timer.Status\n"
+ "kadence-desktop --toggle # same thing from a terminal\n\n"
+ "System Settings → Shortcuts → Custom → D-Bus command binds these to a key."
}
Label {
text: "Kadence " + appVersion + " · sources, blocking rules and reports live on the backend."
font.pixelSize: 11
opacity: 0.6
}
}
}
}
Component.onCompleted: controller.loadSettings()
}