mirror of
https://github.com/arthur-pbty/arthur-os.git
synced 2026-08-04 20:28:23 +02:00
Give reminders an indicator and a full QS flow
This commit is contained in:
@@ -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` |
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,11 @@
|
||||
"label": "Do not disturb",
|
||||
"description": "Notification silencing"
|
||||
},
|
||||
{
|
||||
"value": "Reminder",
|
||||
"label": "Reminder",
|
||||
"description": "Queued reminder status"
|
||||
},
|
||||
{
|
||||
"value": "NightLight",
|
||||
"label": "Night light",
|
||||
|
||||
@@ -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: []
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user