diff --git a/bin/omarchy-notification-send b/bin/omarchy-notification-send index efcd0522..aa1d26fc 100755 --- a/bin/omarchy-notification-send +++ b/bin/omarchy-notification-send @@ -2,7 +2,7 @@ # omarchy:summary=Send an Omarchy desktop notification # omarchy:args=[-g ] [-u ] [description] [notify-send options] -# omarchy:examples=omarchy notification send "Reminder" "5 minutes are up" -g 󰔛 -u critical +# omarchy:examples=omarchy notification send "Reminder" "5 minutes are up" -g 󰢌 set -euo pipefail diff --git a/bin/omarchy-reminder b/bin/omarchy-reminder index f2a95749..d1287101 100755 --- a/bin/omarchy-reminder +++ b/bin/omarchy-reminder @@ -1,8 +1,8 @@ #!/bin/bash # omarchy:summary=Set and show lightweight desktop notification reminders -# omarchy:args= [message] | show | clear -# omarchy:examples=omarchy reminder 5 | omarchy reminder 30 "Check the oven" | omarchy reminder show | omarchy reminder clear +# omarchy:args=[-i|--interactive] | [message] | show [-j|--json] | clear +# omarchy:examples=omarchy reminder -i | omarchy reminder 5 | omarchy reminder 30 "Check the oven" | omarchy reminder show | omarchy reminder show --json | omarchy reminder clear set -euo pipefail @@ -20,31 +20,9 @@ format_remaining() { fi } -parse_systemd_timespan() { - local timespan="$1" - local total=0 - local value unit whole - - while read -r value unit; do - whole=${value%.*} - - case $unit in - d) total=$((total + whole * 86400)) ;; - h) total=$((total + whole * 3600)) ;; - min) total=$((total + whole * 60)) ;; - s) total=$((total + whole)) ;; - ms | us) ;; - esac - done < <(grep -oE '[0-9]+([.][0-9]+)?(d|h|min|s|ms|us)' <<<"$timespan" | sed -E 's/^([0-9.]+)([a-z]+)$/\1 \2/') - - echo "$total" -} - -show_reminders() { - local timer next remaining body="" - local reminder_dir="${XDG_RUNTIME_DIR:-/tmp}/omarchy-reminders" - local reminder_message="" - local now=$(date +%s) +active_reminder_timers() { + local now=${1:-$(date +%s)} + local timer next while IFS=$'\t' read -r timer next; do [[ -z $timer || -z $next ]] && continue @@ -52,10 +30,28 @@ show_reminders() { next=$((next / 1000000)) ((next <= now)) && continue + printf "%s\t%s\n" "$timer" "$next" + done < <(systemctl --user list-timers --all --output=json "omarchy-reminder-*.timer" 2>/dev/null | jq -r '.[] | [.unit, .next] | @tsv') +} + +refresh_indicator() { + omarchy-shell -q omarchy.indicators refresh >/dev/null 2>&1 || true +} + +open_interactive() { + omarchy-shell shell summon omarchy.reminders "{}" +} + +show_reminders() { + local timer next remaining reminder reminder_minutes body="" + local reminder_dir="${XDG_RUNTIME_DIR:-/tmp}/omarchy-reminders" + local reminder_message="" + local now=$(date +%s) + + while IFS=$'\t' read -r timer next; do remaining=$((next - now)) reminder=${timer%.timer} reminder=${reminder#omarchy-reminder-} - set_at=${reminder##*-} reminder_minutes=${reminder%%m-*} reminder_message="" [[ -f $reminder_dir/${timer%.timer}.message ]] && reminder_message=$(<"$reminder_dir/${timer%.timer}.message") @@ -65,15 +61,62 @@ show_reminders() { else body+="${reminder_minutes}-min reminder in $(format_remaining $remaining) ($(date -d "@$next" +%-H:%M))"$'\n' fi - done < <(systemctl --user list-timers --all --output=json "omarchy-reminder-*.timer" 2>/dev/null | jq -r '.[] | [.unit, .next] | @tsv') + done < <(active_reminder_timers "$now") if [[ -z $body ]]; then - omarchy-notification-send -g 󰔛 "Upcoming reminders" "No outstanding reminders" + omarchy-notification-send -g 󰢌 "Upcoming reminders" "No outstanding reminders" else - omarchy-notification-send -g 󰔛 "Upcoming reminders" "${body%$'\n'}" + omarchy-notification-send -g 󰢌 "Upcoming reminders" "${body%$'\n'}" fi } +show_json() { + local timer next remaining reminder reminder_minutes unit reminder_message label item_json reminders_json="[]" + local reminder_dir="${XDG_RUNTIME_DIR:-/tmp}/omarchy-reminders" + local now=$(date +%s) + local count=0 + local tooltip="Set Reminder" + + while IFS=$'\t' read -r timer next; do + count=$((count + 1)) + + unit=${timer%.timer} + reminder=${unit#omarchy-reminder-} + reminder_minutes=${reminder%%m-*} + [[ ! $reminder_minutes =~ ^[0-9]+$ ]] && reminder_minutes=0 + remaining=$((next - now)) + reminder_message="" + [[ -f $reminder_dir/$unit.message ]] && reminder_message=$(<"$reminder_dir/$unit.message") + + if [[ -n $reminder_message ]]; then + label=$reminder_message + else + label="${reminder_minutes}-min reminder" + fi + + item_json=$(jq -cn \ + --arg unit "$unit" \ + --arg timer "$timer" \ + --arg label "$label" \ + --arg message "$reminder_message" \ + --arg remaining "$(format_remaining "$remaining")" \ + --arg atTime "$(date -d "@$next" +%-H:%M)" \ + --argjson minutes "$reminder_minutes" \ + --argjson at "$next" \ + --argjson remainingSeconds "$remaining" \ + '{unit:$unit,timer:$timer,minutes:$minutes,message:$message,label:$label,remaining:$remaining,remainingSeconds:$remainingSeconds,at:$at,atTime:$atTime}') + reminders_json=$(jq -cn --argjson reminders "$reminders_json" --argjson item "$item_json" '$reminders + [$item]') + done < <(active_reminder_timers "$now") + + if ((count == 1)); then + tooltip="1 reminder" + elif ((count > 1)); then + tooltip="$count reminders" + fi + + jq -cn --argjson count "$count" --arg tooltip "$tooltip" --argjson reminders "$reminders_json" '{count:$count,active:($count > 0),tooltip:$tooltip,reminders:$reminders}' +} + clear_reminders() { local units local reminder_dir="${XDG_RUNTIME_DIR:-/tmp}/omarchy-reminders" @@ -85,12 +128,35 @@ clear_reminders() { fi rm -f "$reminder_dir"/omarchy-reminder-*.message 2>/dev/null || true - omarchy-notification-send -g 󰔛 "All reminders have been cleared" + refresh_indicator + omarchy-notification-send -g 󰢌 "All reminders have been cleared" +} + +usage() { + echo "Usage: omarchy-reminder [-i|--interactive]" + echo " omarchy-reminder [message]" + echo " omarchy-reminder show [-j|--json]" + echo " omarchy-reminder clear" } case ${1:-} in +-i | --interactive) + open_interactive + exit 0 + ;; show | list) - show_reminders + case ${2:-} in + -j | --json) + show_json + ;; + "") + show_reminders + ;; + *) + usage + exit 1 + ;; + esac exit 0 ;; clear) @@ -105,9 +171,7 @@ message="$*" custom_message="$message" if [[ -z $minutes ]] || [[ ! $minutes =~ ^[0-9]+$ ]] || ((minutes == 0)); then - echo "Usage: omarchy-reminder [message]" - echo " omarchy-reminder show" - echo " omarchy-reminder clear" + usage exit 1 fi @@ -131,6 +195,7 @@ if [[ -n $custom_message ]]; then fi systemd-run --user --quiet --collect --on-active="${minutes}m" --unit="$unit" \ - bash -c 'omarchy-notification-send -g 󰔛 "Reminder" "$1" -u critical; rm -f "$2"' bash "$message" "$message_file" + bash -c 'omarchy-notification-send -g 󰢌 "Reminder" "$1"; rm -f "$2"; omarchy-shell -q omarchy.indicators refresh >/dev/null 2>&1 || true' bash "$message" "$message_file" -omarchy-notification-send -g 󰔛 "$confirmation_title" "$confirmation" +refresh_indicator +omarchy-notification-send -g 󰢌 "$confirmation_title" "$confirmation" diff --git a/bin/omarchy-reminder-set-interactive b/bin/omarchy-reminder-set-interactive deleted file mode 100755 index 6027f279..00000000 --- a/bin/omarchy-reminder-set-interactive +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -# omarchy:summary=Prompt for a reminder duration + message and schedule it -# omarchy:examples=omarchy reminder set-interactive - -set -e - -prompt_minutes() { - while :; do - minutes=$(omarchy-menu-input "Remind in minutes") || exit 0 - if [[ $minutes =~ ^[0-9]+$ ]] && (( minutes > 0 )); then - printf '%s' "$minutes" - return 0 - fi - [[ -z $minutes ]] && exit 0 - omarchy-notification-send "Invalid reminder" "Enter the number of minutes" -u critical - done -} - -minutes=$(prompt_minutes) -[[ -n $minutes ]] || exit 0 - -message=$(omarchy-menu-input "Reminder message") || exit 0 -if [[ -n $message ]]; then - omarchy-reminder "$minutes" "$message" -else - omarchy-reminder "$minutes" -fi diff --git a/default/omarchy/omarchy-menu.jsonc b/default/omarchy/omarchy-menu.jsonc index 2145a428..b16423ff 100644 --- a/default/omarchy/omarchy-menu.jsonc +++ b/default/omarchy/omarchy-menu.jsonc @@ -43,7 +43,7 @@ "learn.bash": {"icon":"󱆃","label":"Bash","keywords":"shell cheatsheet","action":"omarchy-launch-webapp 'https://devhints.io/bash'"}, // Trigger - "trigger.reminder": {"icon":"󰔛","label":"Reminder","aliases":["reminder"],"keywords":"timer notification remind"}, + "trigger.reminder": {"icon":"󰢌","label":"Reminder","aliases":["reminder"],"keywords":"timer notification remind"}, "trigger.capture": {"icon":"","label":"Capture","aliases":["capture","screenshot","screenrecord","screen-record","screenrecording"],"keywords":"color text extraction"}, "trigger.capture.screenshot": {"icon":"","label":"Screenshot","keywords":"picture","action":"omarchy-capture-screenshot"}, "trigger.capture.screenrecord.stop": {"icon":"","label":"Stop Screenrecording","keywords":"recording video","when":"pgrep -f '^gpu-screen-recorder' >/dev/null","action":"omarchy-capture-screenrecording --stop-recording"}, @@ -67,9 +67,9 @@ "trigger.hardware.touchpad-haptics.mid": {"icon":"󰌌","label":"mid","keywords":"medium","when":"omarchy-hw-dell-xps-haptic-touchpad >/dev/null && omarchy-cmd-present dell-xps-touchpad-haptics","checked":"[[ \"$(dell-xps-touchpad-haptics get)\" == \"mid\" ]]","action":"dell-xps-touchpad-haptics set mid"}, "trigger.hardware.touchpad-haptics.high": {"icon":"󰌌","label":"high","when":"omarchy-hw-dell-xps-haptic-touchpad >/dev/null && omarchy-cmd-present dell-xps-touchpad-haptics","checked":"[[ \"$(dell-xps-touchpad-haptics get)\" == \"high\" ]]","action":"dell-xps-touchpad-haptics set high"}, "trigger.hardware.touchscreen": {"icon":"󰆽","label":"Touchscreen","keywords":"tablet","when":"omarchy-hw-touchscreen >/dev/null","action":"omarchy-toggle-touchscreen"}, - "trigger.reminder.set": {"icon":"󰔛","label":"Set one","aliases":["reminder-set","remind"],"keywords":"timer","action":"omarchy-reminder-set-interactive"}, - "trigger.reminder.show": {"icon":"󰔛","label":"Show all","keywords":"list","action":"omarchy-reminder show"}, - "trigger.reminder.clear": {"icon":"󰔛","label":"Clear all","keywords":"delete","action":"omarchy-reminder clear"}, + "trigger.reminder.set": {"icon":"󰢌","label":"Set one","aliases":["reminder-set","remind"],"keywords":"timer","action":"omarchy-reminder -i"}, + "trigger.reminder.show": {"icon":"󰢌","label":"Show all","keywords":"list","action":"omarchy-reminder show"}, + "trigger.reminder.clear": {"icon":"󰢌","label":"Clear all","keywords":"delete","action":"omarchy-reminder clear"}, "trigger.share.clipboard": {"icon":"","label":"Clipboard","action":"omarchy-menu-share clipboard"}, "trigger.share.file": {"icon":"","label":"File","action":"xdg-terminal-exec --app-id=org.omarchy.terminal bash -c 'omarchy-menu-share file'"}, "trigger.share.folder": {"icon":"","label":"Folder","keywords":"directory","action":"xdg-terminal-exec --app-id=org.omarchy.terminal bash -c 'omarchy-menu-share folder'"}, diff --git a/shell/plugins/README.md b/shell/plugins/README.md index 9decd141..d98b8fd2 100644 --- a/shell/plugins/README.md +++ b/shell/plugins/README.md @@ -17,6 +17,7 @@ User-installed plugins live alongside these conceptually but on disk under | Image picker | `omarchy.image-picker` | `overlay` | `image-picker/ImagePicker.qml` | | Emojis | `omarchy.emojis` | `overlay` | `emojis/Emojis.qml` | | Clipboard mgr | `omarchy.clipboard` | `overlay` | `clipboard/Clipboard.qml` | +| Reminders | `omarchy.reminders` | `overlay` | `reminders/ReminderFlow.qml` | | Omarchy menu | `omarchy.menu` | `menu`, `bar-widget` | `menu/Menu.qml`, `menu/BarWidget.qml` | | Notifications | `omarchy.notifications` | `service`, `bar-widget` | `notifications/Service.qml`, `notifications/BarWidget.qml` | | Media | `omarchy.media` | `service`, `bar-widget` | `services/media/Service.qml`, `services/media/BarWidget.qml` | diff --git a/shell/plugins/bar/README.md b/shell/plugins/bar/README.md index fabc0556..cb84854a 100644 --- a/shell/plugins/bar/README.md +++ b/shell/plugins/bar/README.md @@ -72,7 +72,7 @@ Example `shell.json` (bar subtree only shown): | `omarchy.bluetooth` | Bluetooth icon + popup with device list, connect/disconnect, battery | left = popup · right = toggle radio · middle = bluetoothctl TUI | | `omarchy.monitor` | Brightness and laptop display controls | left = popup | -The `omarchy.indicators` widget loads individual bar indicators from `indicators/`. Omit `items` (or set it to an empty array) to show all indicators in the default order, or set `items` to a subset such as `["Dnd", "NightLight"]`. Set `alwaysShow` to `true` to keep inactive indicators visible instead of revealing them only on hover. Multiple `omarchy.indicators` instances are allowed, so different sections can show different subsets. +The `omarchy.indicators` widget loads individual bar indicators from `indicators/`. Omit `items` (or set it to an empty array) to show all indicators in the default order, or set `items` to a subset such as `["Dnd", "Reminder", "NightLight"]`. Set `alwaysShow` to `true` to keep inactive indicators visible instead of revealing them only on hover. Multiple `omarchy.indicators` instances are allowed, so different sections can show different subsets. ## Orientation diff --git a/shell/plugins/bar/indicators/Reminder.qml b/shell/plugins/bar/indicators/Reminder.qml new file mode 100644 index 00000000..3441edbe --- /dev/null +++ b/shell/plugins/bar/indicators/Reminder.qml @@ -0,0 +1,59 @@ +import QtQuick +import Quickshell +import Quickshell.Io +import qs.Ui + +BarIndicator { + id: root + + property int reminderCount: 0 + property string tooltip: "" + + active: reminderCount > 0 + activeText: "󰢌" + inactiveText: "󰢌" + activeTooltipText: tooltip + inactiveTooltipText: tooltip + + function refresh() { + if (!jsonProc.running) jsonProc.running = true + } + + function openReminderFlow() { + Quickshell.execDetached(["omarchy-reminder", "-i"]) + } + + function update(raw) { + var data = extractData(raw) + reminderCount = Number(data.count || 0) + tooltip = String(data.tooltip || "") + } + + Component.onCompleted: refresh() + + Connections { + target: root.indicatorHost + ignoreUnknownSignals: true + function onRefreshRequested() { root.refresh() } + } + + Process { + id: jsonProc + command: ["omarchy-reminder", "show", "--json"] + stdout: StdioCollector { + waitForEnd: true + onStreamFinished: root.update(text) + } + onExited: function(exitCode) { + if (exitCode !== 0) { + root.reminderCount = 0 + root.tooltip = "" + } + } + } + + onPressed: function() { + if (root.reminderCount > 0) Quickshell.execDetached(["omarchy-reminder", "show"]) + else root.openReminderFlow() + } +} diff --git a/shell/plugins/bar/widgets/Indicators.manifest.json b/shell/plugins/bar/widgets/Indicators.manifest.json index c8c49218..858e758d 100644 --- a/shell/plugins/bar/widgets/Indicators.manifest.json +++ b/shell/plugins/bar/widgets/Indicators.manifest.json @@ -31,6 +31,11 @@ "label": "Do not disturb", "description": "Notification silencing" }, + { + "value": "Reminder", + "label": "Reminder", + "description": "Queued reminder status" + }, { "value": "NightLight", "label": "Night light", diff --git a/shell/plugins/bar/widgets/Indicators.qml b/shell/plugins/bar/widgets/Indicators.qml index a4cd84f2..644f2833 100644 --- a/shell/plugins/bar/widgets/Indicators.qml +++ b/shell/plugins/bar/widgets/Indicators.qml @@ -9,7 +9,7 @@ BarWidget { moduleName: "omarchy.indicators" readonly property int indicatorSlotExtent: Style.space(22) - readonly property var defaultIndicatorEntries: [ "Dnd", "NightLight", "StayAwake", "ScreenRecording", "Dictation" ] + readonly property var defaultIndicatorEntries: [ "Dnd", "Reminder", "NightLight", "StayAwake", "ScreenRecording", "Dictation" ] readonly property int inactiveSlotExtent: indicatorEntries.length * indicatorSlotExtent readonly property var indicatorEntries: indicatorEntriesFromSettings(settings) property var activeIndicatorIds: [] diff --git a/shell/plugins/menu/Menu.qml b/shell/plugins/menu/Menu.qml index 30dc8bb5..1e06c4ab 100644 --- a/shell/plugins/menu/Menu.qml +++ b/shell/plugins/menu/Menu.qml @@ -116,6 +116,13 @@ Item { resultProc.running = true } + function runAction(action) { + var command = String(action || "") + if (!command) return + + Quickshell.execDetached(["bash", "-lc", command]) + } + function rowHeightForDetail(detail) { return root.filterText && detail ? root.detailRowHeight : root.baseRowHeight } @@ -733,10 +740,11 @@ Item { function applySelected(id, action) { if (!id) { cancel(); return } + applySerial = requestSerial opened = false filterText = "" - if (action) Quickshell.execDetached(["bash", "-lc", action]) + root.runAction(action) } function cancel() { @@ -815,7 +823,7 @@ Item { // instead of opening an action with no children. if (entry && entry.kind === "action" && entry.action) { root.cancel() - Quickshell.execDetached(["bash", "-lc", entry.action]) + root.runAction(entry.action) return "ok" } // If it's a link (a redirect to another menu), follow the link. diff --git a/shell/plugins/reminders/ReminderFlow.qml b/shell/plugins/reminders/ReminderFlow.qml new file mode 100644 index 00000000..d6dbd0ca --- /dev/null +++ b/shell/plugins/reminders/ReminderFlow.qml @@ -0,0 +1,168 @@ +import Quickshell +import Quickshell.Wayland +import QtQuick +import qs.Commons + +Item { + id: root + + property var shell: null + property var manifest: null + + property bool opened: false + property string step: "minutes" + property string minutes: "" + property string filterText: "" + property string fontFamily: Style.font.menuFamily + + property color background: Color.menu.background + property color foreground: Color.menu.text + property color border: Color.menu.border + property color scrim: Color.menu.scrim + readonly property int cornerRadius: Style.cornerRadius + property int contentMargin: Style.spacing.panelPadding + property int headerHeight: Math.max(Style.space(34), Style.font.title + Style.spacing.controlPaddingY * 2) + property int cardWidth: Math.min(Style.space(300), panel.width - Style.gapsOut * 2) + property int cardHeight: Math.min(contentMargin * 2 + headerHeight, panel.height - Style.gapsOut * 2) + readonly property string promptText: root.step === "message" ? "Reminder message" : "Remind in minutes" + + function open(payloadJson) { + var payload = ({}) + try { payload = JSON.parse(payloadJson || "{}") } catch (e) { payload = ({}) } + if (payload.fontFamily) root.fontFamily = payload.fontFamily + + root.opened = true + root.step = "minutes" + root.minutes = "" + root.filterText = "" + + Qt.callLater(function() { keyCatcher.forceActiveFocus() }) + } + + function close() { + root.opened = false + } + + function dismiss() { + root.opened = false + if (root.shell && typeof root.shell.hide === "function") + root.shell.hide((root.manifest && root.manifest.id) || "omarchy.reminders") + } + + function toggle() { + if (root.opened) root.dismiss() + else root.open("{}") + } + + function setFilter(nextFilter) { + root.filterText = nextFilter + } + + function submit() { + var selection = root.filterText + + if (root.step === "minutes") { + var nextMinutes = selection.trim() + + if (!nextMinutes) { + root.dismiss() + return + } + + if (!/^[0-9]+$/.test(nextMinutes) || Number(nextMinutes) <= 0) { + Quickshell.execDetached(["bash", "-lc", "omarchy-notification-send 'Invalid reminder' 'Enter the number of minutes'"]) + return + } + + root.minutes = nextMinutes + root.step = "message" + root.filterText = "" + Qt.callLater(function() { keyCatcher.forceActiveFocus() }) + return + } + + if (root.step === "message") { + var command = "omarchy-reminder " + Util.shellQuote(root.minutes) + if (selection.length > 0) + command += " " + Util.shellQuote(selection) + + root.dismiss() + Quickshell.execDetached(["bash", "-lc", command]) + } + } + + PanelWindow { + id: panel + visible: root.opened + anchors { top: true; bottom: true; left: true; right: true } + color: "transparent" + WlrLayershell.namespace: "omarchy-reminders" + WlrLayershell.layer: WlrLayer.Overlay + WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive + exclusionMode: ExclusionMode.Ignore + + Rectangle { + anchors.fill: parent + color: root.scrim + } + + MouseArea { + anchors.fill: parent + onClicked: root.dismiss() + } + + Rectangle { + id: card + width: root.cardWidth + height: root.cardHeight + radius: root.cornerRadius + anchors.centerIn: parent + color: root.background + border.color: root.border + border.width: Math.max(1, Style.space(2)) + + MouseArea { anchors.fill: parent; onClicked: {} } + + Item { + id: keyCatcher + anchors.fill: parent + focus: true + + Keys.priority: Keys.BeforeItem + Keys.onPressed: function(event) { + if (event.key === Qt.Key_Escape) { + if (root.filterText) root.setFilter("") + else root.dismiss() + event.accepted = true + } else if (event.key === Qt.Key_Backspace) { + if (root.filterText.length > 0) root.setFilter(root.filterText.slice(0, -1)) + event.accepted = true + } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + root.submit() + event.accepted = true + } else if (event.text && event.text.length === 1 && event.text.charCodeAt(0) >= 32 && event.text.charCodeAt(0) !== 127) { + root.setFilter(root.filterText + event.text) + event.accepted = true + } + } + } + + Item { + anchors.fill: parent + anchors.margins: root.contentMargin + + Text { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + text: root.filterText || (root.promptText + "...") + color: root.foreground + opacity: root.filterText ? 1 : 0.58 + font.family: root.fontFamily + font.pixelSize: Style.font.heading + elide: Text.ElideRight + } + } + } + } +} diff --git a/shell/plugins/reminders/manifest.json b/shell/plugins/reminders/manifest.json new file mode 100644 index 00000000..5745d444 --- /dev/null +++ b/shell/plugins/reminders/manifest.json @@ -0,0 +1,15 @@ +{ + "schemaVersion": 1, + "id": "omarchy.reminders", + "name": "Reminders", + "version": "1.0.0", + "author": "Omarchy", + "description": "Interactive reminder setup flow", + "kinds": [ + "overlay" + ], + "keepLoaded": true, + "entryPoints": { + "overlay": "ReminderFlow.qml" + } +}