a lightweight, interval-based utility to combat digital strain through "Ma" (intentional pauses) for the eyes and body.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: move break behavior to rhythm tab, cleanup and rename profile tab

+186 -206
+7 -7
ui/settings.slint
··· 80 80 } 81 81 82 82 TabButton { 83 - text: "Profile"; 83 + text: "Preferences"; 84 84 active: root.active-tab == "profile"; 85 85 clicked => { root.active-tab = "profile"; } 86 86 } ··· 106 106 long-break-duration-mins <=> root.long-break-duration-mins; 107 107 long-break-gap-mins <=> root.long-break-gap-mins; 108 108 long-break-label <=> root.long-break-label; 109 + enforced-mode <=> root.enforced-mode; 110 + idle-detection-enabled <=> root.idle-detection-enabled; 111 + idle-threshold-mins <=> root.idle-threshold-mins; 112 + password-is-set <=> root.password-is-set; 109 113 profile-changed(name) => { root.profile-changed(name); } 110 114 level-changed(i, entry) => { root.level-changed(i, entry); } 111 115 level-removed(i) => { root.level-removed(i); } 112 116 level-added => { root.level-added(); } 117 + set-password-clicked => { root.set-password-clicked(); } 118 + enforced-mode-toggled(checked) => { root.enforced-mode-toggled(checked); } 113 119 } 114 120 115 121 if root.active-tab == "profile": ProfileTab { 116 - enforced-mode <=> root.enforced-mode; 117 122 sound-enabled <=> root.sound-enabled; 118 123 sound-volume <=> root.sound-volume; 119 124 autostart <=> root.autostart; 120 - idle-detection-enabled <=> root.idle-detection-enabled; 121 - idle-threshold-mins <=> root.idle-threshold-mins; 122 125 theme-mode <=> root.theme-mode; 123 126 text-size-mode <=> root.text-size-mode; 124 - password-is-set <=> root.password-is-set; 125 - set-password-clicked => { root.set-password-clicked(); } 126 - enforced-mode-toggled(checked) => { root.enforced-mode-toggled(checked); } 127 127 open-config-dir => { root.open-config-dir(); } 128 128 theme-mode-changed(i) => { root.theme-mode-changed(i); } 129 129 text-size-mode-changed(i) => { root.text-size-mode-changed(i); }
+79 -198
ui/views/profile_tab.slint
··· 1 1 import { ScrollView } from "std-widgets.slint"; 2 2 import { Theme } from "../theme.slint"; 3 - import { SettingLabel, SectionHeading } from "../components/atoms.slint"; 3 + import { SettingLabel } from "../components/atoms.slint"; 4 4 import { PaperButton } from "../components/buttons.slint"; 5 - import { NumberField, VolumeSlider, PaperComboBox } from "../components/inputs.slint"; 5 + import { VolumeSlider, PaperComboBox } from "../components/inputs.slint"; 6 6 import { PaperToggle } from "../components/toggles.slint"; 7 7 import { ChipGroup } from "../components/chips.slint"; 8 8 9 9 export component ProfileTab inherits ScrollView { 10 - in-out property <bool> enforced-mode: false; 11 10 in-out property <bool> sound-enabled: true; 12 11 in-out property <float> sound-volume: 0.7; 13 12 in-out property <bool> autostart: false; 14 - in-out property <bool> idle-detection-enabled: true; 15 - in-out property <int> idle-threshold-mins: 5; 16 13 in-out property <int> theme-mode: 0; 17 14 in-out property <int> text-size-mode: 0; 18 - in-out property <bool> password-is-set: false; 19 15 20 - callback set-password-clicked; 21 16 callback open-config-dir; 22 17 callback theme-mode-changed(int); 23 18 callback text-size-mode-changed(int); 24 - callback enforced-mode-toggled(bool); 25 19 26 20 vertical-stretch: 1; 27 21 ··· 32 26 padding-bottom: 16px; 33 27 spacing: 0px; 34 28 35 - SectionHeading { 36 - title: "During breaks"; 37 - } 29 + HorizontalLayout { 30 + padding-top: 12px; 31 + padding-bottom: 4px; 32 + spacing: 24px; 38 33 39 - Rectangle { height: 12px; } 34 + SettingLabel { 35 + title: "Chime on break start"; 36 + } 40 37 41 - VerticalLayout { 42 - spacing: 0px; 43 - 44 - HorizontalLayout { 45 - padding-top: 12px; 46 - padding-bottom: 4px; 47 - spacing: 24px; 48 - 49 - SettingLabel { 50 - title: "Enforced mode"; 51 - description: "Full-screen break. Emergency unlock requires a password."; 52 - } 53 - 54 - Rectangle { horizontal-stretch: 1; } 38 + Rectangle { horizontal-stretch: 1; } 55 39 56 - PaperToggle { 57 - checked: root.enforced-mode; 58 - label: "Enforced mode"; 59 - toggled(checked) => { 60 - if !checked && root.password-is-set { 61 - // Revert — Rust side will verify password first 62 - root.enforced-mode-toggled(false); 63 - } else { 64 - root.enforced-mode = checked; 65 - } 66 - } 67 - } 40 + PaperToggle { 41 + checked <=> root.sound-enabled; 42 + label: "Chime on break start"; 68 43 } 44 + } 69 45 70 - if root.enforced-mode: HorizontalLayout { 71 - padding-bottom: 12px; 46 + if root.sound-enabled: HorizontalLayout { 47 + padding-bottom: 12px; 48 + spacing: 12px; 49 + alignment: center; 72 50 73 - PaperButton { 74 - text: root.password-is-set ? "Change emergency password…" : "Set emergency unlock password…"; 75 - clicked => { root.set-password-clicked(); } 76 - } 51 + Text { 52 + text: "volume"; 53 + font-size: Theme.font_label; 54 + color: Theme.ink-mid; 55 + vertical-alignment: center; 77 56 } 78 57 79 - HorizontalLayout { 80 - padding-top: 12px; 81 - padding-bottom: 4px; 82 - spacing: 24px; 83 - 84 - SettingLabel { 85 - title: "Pause on idle"; 86 - description: "If you've stepped away, ioma waits."; 87 - } 88 - 89 - Rectangle { horizontal-stretch: 1; } 90 - 91 - PaperToggle { 92 - checked <=> root.idle-detection-enabled; 93 - label: "Pause on idle"; 94 - } 58 + VolumeSlider { 59 + value <=> root.sound-volume; 60 + horizontal-stretch: 1; 95 61 } 96 62 97 - if root.idle-detection-enabled: HorizontalLayout { 98 - padding-bottom: 12px; 99 - spacing: 10px; 100 - alignment: start; 101 - 102 - Text { 103 - text: "for"; 104 - font-size: Theme.font_label; 105 - color: Theme.ink-mid; 106 - vertical-alignment: center; 107 - } 108 - 109 - NumberField { 110 - width: 80px; 111 - value <=> root.idle-threshold-mins; 112 - minimum: 1; 113 - maximum: 60; 114 - field-label: "Idle threshold minutes"; 115 - } 116 - 117 - Text { 118 - text: "minutes before resetting"; 119 - font-size: Theme.font_label; 120 - color: Theme.ink-mid; 121 - vertical-alignment: center; 122 - } 63 + Text { 64 + text: "\{Math.round(root.sound-volume * 100)}%"; 65 + min-width: 44px; 66 + font-size: Theme.font_label; 67 + font-weight: 600; 68 + color: Theme.ink; 69 + horizontal-alignment: right; 70 + vertical-alignment: center; 123 71 } 124 72 } 125 73 126 - Rectangle { height: 28px; } 127 - 128 - SectionHeading { 129 - title: "Sensory experience"; 130 - } 74 + HorizontalLayout { 75 + padding-top: 12px; 76 + padding-bottom: 10px; 77 + spacing: 24px; 131 78 132 - Rectangle { height: 12px; } 133 - 134 - VerticalLayout { 135 - spacing: 0px; 136 - 137 - HorizontalLayout { 138 - padding-top: 12px; 139 - padding-bottom: 4px; 140 - spacing: 24px; 141 - 142 - SettingLabel { 143 - title: "Chime on break start"; 144 - } 145 - 146 - Rectangle { horizontal-stretch: 1; } 147 - 148 - PaperToggle { 149 - checked <=> root.sound-enabled; 150 - label: "Chime on break start"; 151 - } 79 + SettingLabel { 80 + title: "Appearance"; 152 81 } 153 82 154 - if root.sound-enabled: HorizontalLayout { 155 - padding-bottom: 12px; 156 - spacing: 12px; 157 - alignment: center; 83 + Rectangle { horizontal-stretch: 1; } 158 84 159 - Text { 160 - text: "volume"; 161 - font-size: Theme.font_label; 162 - color: Theme.ink-mid; 163 - vertical-alignment: center; 164 - } 165 - 166 - VolumeSlider { 167 - value <=> root.sound-volume; 168 - horizontal-stretch: 1; 169 - } 170 - 171 - Text { 172 - text: "\{Math.round(root.sound-volume * 100)}%"; 173 - min-width: 44px; 174 - font-size: Theme.font_label; 175 - font-weight: 600; 176 - color: Theme.ink; 177 - horizontal-alignment: right; 178 - vertical-alignment: center; 85 + ChipGroup { 86 + labels: ["System", "Light", "Dark"]; 87 + selected-index: root.theme-mode; 88 + selection-changed(i) => { 89 + root.theme-mode = i; 90 + root.theme-mode-changed(i); 179 91 } 180 92 } 181 - 182 - HorizontalLayout { 183 - padding-top: 12px; 184 - padding-bottom: 10px; 185 - spacing: 24px; 186 - 187 - SettingLabel { 188 - title: "Appearance"; 189 - } 93 + } 190 94 191 - Rectangle { horizontal-stretch: 1; } 95 + HorizontalLayout { 96 + padding-top: 12px; 97 + padding-bottom: 10px; 98 + spacing: 24px; 192 99 193 - ChipGroup { 194 - labels: ["System", "Light", "Dark"]; 195 - selected-index: root.theme-mode; 196 - selection-changed(i) => { 197 - root.theme-mode = i; 198 - root.theme-mode-changed(i); 199 - } 200 - } 100 + SettingLabel { 101 + title: "Text size"; 201 102 } 202 103 203 - HorizontalLayout { 204 - padding-top: 12px; 205 - padding-bottom: 10px; 206 - spacing: 24px; 104 + Rectangle { horizontal-stretch: 1; } 207 105 208 - SettingLabel { 209 - title: "Text size"; 210 - } 211 - 212 - Rectangle { horizontal-stretch: 1; } 213 - 214 - PaperComboBox { 215 - model: ["Compact", "Small", "Default", "Large", "Extra Large"]; 216 - selected-index: root.text-size-mode; 217 - field-label: "Text size"; 218 - selection-changed(i) => { 219 - root.text-size-mode = i; 220 - root.text-size-mode-changed(i); 221 - } 106 + PaperComboBox { 107 + model: ["Compact", "Small", "Default", "Large", "Extra Large"]; 108 + selected-index: root.text-size-mode; 109 + field-label: "Text size"; 110 + selection-changed(i) => { 111 + root.text-size-mode = i; 112 + root.text-size-mode-changed(i); 222 113 } 223 114 } 224 115 } 225 116 226 117 Rectangle { height: 28px; } 227 118 228 - SectionHeading { 229 - title: "On this computer"; 230 - } 119 + HorizontalLayout { 120 + padding-top: 12px; 121 + padding-bottom: 10px; 122 + spacing: 24px; 231 123 232 - Rectangle { height: 12px; } 124 + SettingLabel { 125 + title: "Launch with system"; 126 + } 233 127 234 - VerticalLayout { 235 - spacing: 0px; 236 - 237 - HorizontalLayout { 238 - padding-top: 12px; 239 - padding-bottom: 10px; 240 - spacing: 24px; 128 + Rectangle { horizontal-stretch: 1; } 241 129 242 - SettingLabel { 243 - title: "Launch with system"; 244 - } 245 - 246 - Rectangle { horizontal-stretch: 1; } 247 - 248 - PaperToggle { 249 - checked <=> root.autostart; 250 - label: "Launch with system"; 251 - } 130 + PaperToggle { 131 + checked <=> root.autostart; 132 + label: "Launch with system"; 252 133 } 134 + } 253 135 254 - HorizontalLayout { 255 - padding-bottom: 12px; 136 + HorizontalLayout { 137 + padding-bottom: 12px; 256 138 257 - PaperButton { 258 - text: "Open config file location"; 259 - clicked => { root.open-config-dir(); } 260 - } 139 + PaperButton { 140 + text: "Open config file location"; 141 + clicked => { root.open-config-dir(); } 261 142 } 262 143 } 263 144
+100 -1
ui/views/rhythm_tab.slint
··· 1 1 import { ScrollView } from "std-widgets.slint"; 2 2 import { Theme } from "../theme.slint"; 3 - import { SettingLabel } from "../components/atoms.slint"; 3 + import { SettingLabel, SectionHeading } from "../components/atoms.slint"; 4 + import { PaperButton } from "../components/buttons.slint"; 4 5 import { PaperToggle } from "../components/toggles.slint"; 5 6 import { NumberField, PaperInput, PaperComboBox } from "../components/inputs.slint"; 6 7 import { IntervalCard, LevelEntry } from "../components/interval_card.slint"; ··· 17 18 in-out property <int> long-break-duration-mins: 30; 18 19 in-out property <int> long-break-gap-mins: 30; 19 20 in-out property <string> long-break-label: "Long rest"; 21 + in-out property <bool> enforced-mode: false; 22 + in-out property <bool> idle-detection-enabled: true; 23 + in-out property <int> idle-threshold-mins: 5; 24 + in-out property <bool> password-is-set: false; 20 25 21 26 callback level-changed(int, LevelEntry); 22 27 callback level-removed(int); 23 28 callback level-added; 24 29 callback profile-changed(string); 30 + callback set-password-clicked; 31 + callback enforced-mode-toggled(bool); 25 32 26 33 private property <int> profile-index: 27 34 root.active-profile == root.profile-names[0] ? 0 : ··· 246 253 Text { 247 254 text: "min away."; 248 255 font-size: Theme.font_xsmall; 256 + color: Theme.ink-mid; 257 + vertical-alignment: center; 258 + } 259 + } 260 + } 261 + 262 + Rectangle { height: 28px; } 263 + 264 + SectionHeading { 265 + title: "Break behavior"; 266 + } 267 + 268 + Rectangle { height: 12px; } 269 + 270 + VerticalLayout { 271 + spacing: 0px; 272 + 273 + HorizontalLayout { 274 + padding-top: 12px; 275 + padding-bottom: 4px; 276 + spacing: 24px; 277 + 278 + SettingLabel { 279 + title: "Enforced mode"; 280 + description: "Full-screen break. Emergency unlock requires a password."; 281 + } 282 + 283 + Rectangle { horizontal-stretch: 1; } 284 + 285 + PaperToggle { 286 + checked: root.enforced-mode; 287 + label: "Enforced mode"; 288 + toggled(checked) => { 289 + if !checked && root.password-is-set { 290 + root.enforced-mode-toggled(false); 291 + } else { 292 + root.enforced-mode = checked; 293 + } 294 + } 295 + } 296 + } 297 + 298 + if root.enforced-mode: HorizontalLayout { 299 + padding-bottom: 12px; 300 + 301 + PaperButton { 302 + text: root.password-is-set ? "Change emergency password…" : "Set emergency unlock password…"; 303 + clicked => { root.set-password-clicked(); } 304 + } 305 + } 306 + 307 + HorizontalLayout { 308 + padding-top: 12px; 309 + padding-bottom: 4px; 310 + spacing: 24px; 311 + 312 + SettingLabel { 313 + title: "Pause on idle"; 314 + description: "If you've stepped away, ioma waits."; 315 + } 316 + 317 + Rectangle { horizontal-stretch: 1; } 318 + 319 + PaperToggle { 320 + checked <=> root.idle-detection-enabled; 321 + label: "Pause on idle"; 322 + } 323 + } 324 + 325 + if root.idle-detection-enabled: HorizontalLayout { 326 + padding-bottom: 12px; 327 + spacing: 10px; 328 + alignment: start; 329 + 330 + Text { 331 + text: "for"; 332 + font-size: Theme.font_label; 333 + color: Theme.ink-mid; 334 + vertical-alignment: center; 335 + } 336 + 337 + NumberField { 338 + width: 80px; 339 + value <=> root.idle-threshold-mins; 340 + minimum: 1; 341 + maximum: 60; 342 + field-label: "Idle threshold minutes"; 343 + } 344 + 345 + Text { 346 + text: "minutes before resetting"; 347 + font-size: Theme.font_label; 249 348 color: Theme.ink-mid; 250 349 vertical-alignment: center; 251 350 }