Compare commits

...
7 Commits
9 changed files with 324 additions and 17 deletions
+1 -1
View File
@@ -28,7 +28,7 @@ My current setup is built around these core components:
- **`scripts/`**: Global utility scripts seamlessly exposed as commands by the installer. See [`commands.md`](commands.md).
- **`wallpapers/`**: A collection of local custom wallpapers for dynamic theming. See [`wallpaper-gallery/`](wallpaper-gallery/index.md) for the full gallery (split alphabetically across multiple pages).
- **`themes/`**: Drop-in local color themes (one `colors.toml` per theme) that `install.sh` deploys into Omarchy's theme directory. See [`themes/README.md`](themes/README.md).
- **`branding/`**: Custom ASCII art and system branding assets.
- **`branding/`**: Custom ASCII art and system branding assets. `blob_icon.svg` is the bar menu button.
- **`kitty/`**: kitty terminal config and its color theme, applied by hand rather than by `install.sh`. See [`kitty/README.md`](kitty/README.md).
## Docs
+19
View File
@@ -57,6 +57,11 @@ const [stayAwake, setStayAwake] = pollBool(
2000,
)
const [tabletFollow, setTabletFollow] = pollBool(
"omarchy-toggle-tablet-follow status 2>/dev/null | grep -q '\"enabled\":true' && echo on || echo off",
2000,
)
const [recording] = pollBool(
"pgrep -f '^gpu-screen-recorder' >/dev/null && echo on || echo off",
2000,
@@ -219,6 +224,20 @@ function Toggles() {
}}
/>
</box>
<box class="qs-tiles" spacing={8} homogeneous>
<Tile
icon={""}
label="Tablet Lock"
active={tabletFollow}
tooltip={tabletFollow.as((on: boolean) =>
on ? "Tablet follows focused monitor" : "Tablet spans all monitors",
)}
onClicked={() => {
setTabletFollow(!tabletFollow.get())
sh("omarchy-toggle-tablet-follow toggle")
}}
/>
</box>
</box>
)
}
+25
View File
@@ -0,0 +1,25 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="520.000000pt" height="511.000000pt" viewBox="0 0 520.000000 511.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,511.000000) scale(0.050000,-0.050000)"
fill="#000000" stroke="none">
<path d="M4790 10147 c-3157 -250 -5335 -3216 -4578 -6237 784 -3132 4321
-4735 7258 -3290 2905 1429 3734 5102 1723 7628 -1028 1292 -2741 2030 -4403
1899z m1090 -337 c3940 -604 5501 -5305 2677 -8061 -2334 -2278 -6268 -1614
-7707 1301 -1482 3002 510 6440 3940 6798 173 18 888 -7 1090 -38z"/>
<path d="M2810 8049 c-22 -5 -89 -19 -150 -31 -998 -194 -1377 -1466 -652
-2188 1078 -1073 2875 139 2209 1490 -238 483 -926 840 -1407 729z m550 -269
c782 -263 1032 -1216 470 -1788 -633 -642 -1757 -350 -1959 510 -195 826 658
1558 1489 1278z"/>
<path d="M2626 7544 c-486 -174 -571 -762 -147 -1018 572 -345 1209 298 812
819 -142 185 -447 277 -665 199z"/>
<path d="M6930 8029 c-1037 -228 -1420 -1465 -682 -2199 855 -850 2351 -270
2351 910 0 832 -830 1473 -1669 1289z m689 -256 c777 -270 1012 -1233 437
-1796 -638 -624 -1745 -325 -1945 525 -197 836 666 1564 1508 1271z"/>
<path d="M7295 7073 c-373 -105 -571 -506 -415 -839 286 -608 1249 -418 1249
246 0 411 -420 710 -834 593z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+2
View File
@@ -1,2 +1,4 @@
-- Extra autostart processes.
o.launch_on_start("ags run")
o.launch_on_start("omarchy-toggle-tablet-follow autostart")
+15 -7
View File
@@ -23,7 +23,7 @@ Ported from the old `waybar/config.jsonc`, section for section:
| `network` | `omarchy.network` |
| `pulseaudio` | `omarchy.audio` |
| `battery` | `omarchy.power` |
| `cpu` | `blob.cpu` (custom module) |
| `cpu` | dropped (btop and the AGS system monitor have no bar entry point now) |
| `custom/notification` | `blob.notifications` (custom module) |
| (new in Omarchy 4) | `omarchy.agents` - AI agent usage |
| (new in Omarchy 4) | `omarchy.monitor` - brightness and display controls |
@@ -52,11 +52,9 @@ clears it. The lock screen carries the branding on its own (see
## Custom modules
The bar accepts arbitrary ids with `type: "command"`, which is how the two
AGS entry points survive the move off waybar:
The bar accepts arbitrary ids with `type: "command"`, which is how the AGS
entry points survive the move off waybar:
- `blob.cpu` - left opens btop, middle opens alacritty, right toggles the AGS
system monitor
- `blob.notifications` - left toggles the AGS notification center, right
toggles notification silencing
- `blob.clock` - left toggles AGS quick settings, middle opens the shell's own
@@ -78,14 +76,24 @@ by hand; see below for why.
hardcodes workspaces 1-5 as always visible and has no setting for it, so the
clone changes that list to 1-9 to match the old waybar `persistent-workspaces`.
`plugins/blob.menu/` clones `omarchy.menu` to widen it and to let a menu row
choose where it sits. `cardWidth` in `Menu.qml` is hardcoded at
`plugins/blob.menu/` clones `omarchy.menu` to widen it, to let a menu row
choose where it sits, and to put the Blob icon on the bar button. `cardWidth` in `Menu.qml` is hardcoded at
`Style.space(300)`; the clone raises it to 440, so the apps menu (Super + Space)
and the root menu (Super + Alt + Space) are both wider. The two oversized menus
(screen recording, font picker) keep their own 520 and are untouched.
`omarchy-menu` still targets `omarchy.menu` on the CLI - the manifest records
`clonedFrom`, and the shell routes those calls here.
`BarWidget.qml` swaps the stock `\ue900` glyph from the `omarchy` icon font for
`branding/blob_icon.svg`. An SVG is text, so the fill is rewritten to the bar
foreground in QML and the result handed to `Image` as a base64 `data:` URL -
the same trick the `theme-set` hook uses with `sed` for the fastfetch logo, so
both consumers of the icon work the same way. The color is read off
`button.foreground` rather than the theme, which keeps the icon on the bar's own
foreground animation, and rebuilding the URL is what reloads the image, so there
is no image cache to bust. The glyph stays as the `text` fallback and is shown
whenever the SVG cannot be read or decoded, so the button is never blank.
`mergeMenuSources` in `MenuModel.js` reads the default menu first and the user
extension second, so a row that only exists in `extensions/omarchy-menu.jsonc`
lands at the bottom of its menu with no way to move it. The clone adds a
+43
View File
@@ -1,24 +1,67 @@
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Ui
BarWidget {
id: root
moduleName: "omarchy.menu"
readonly property string iconPath: Quickshell.env("HOME") + "/.config/omarchy/branding/blob_icon.svg"
readonly property int iconSize: Math.round(Style.font.body * 1.35)
property string iconSvg: ""
readonly property string iconColor: hexColor(button.foreground)
readonly property string tintedSvg: iconSvg.replace(/fill="#000000"/g, 'fill="' + iconColor + '"')
readonly property string iconUrl: iconSvg.length > 0 ? "data:image/svg+xml;base64," + Qt.btoa(tintedSvg) : ""
readonly property bool iconReady: icon.status === Image.Ready
function hexColor(value) {
function channel(fraction) {
return ("0" + Math.round(fraction * 255).toString(16)).slice(-2)
}
return "#" + channel(value.r) + channel(value.g) + channel(value.b)
}
implicitWidth: button.implicitWidth
implicitHeight: button.implicitHeight
FileView {
path: root.iconPath
watchChanges: true
printErrors: false
onLoaded: root.iconSvg = text()
onLoadFailed: root.iconSvg = ""
onFileChanged: reload()
}
WidgetButton {
id: button
anchors.fill: parent
bar: root.bar
text: "\ue900"
fontFamily: "omarchy"
labelVisible: !root.iconReady
fixedWidth: root.iconReady ? root.iconSize + Style.spaceReal(15) : -1
horizontalMargin: 7.5
onPressed: function(button) {
if (!root.bar) return
if (button === Qt.RightButton) root.bar.run("xdg-terminal-exec")
else root.bar.run("omarchy-shell shell toggle omarchy.menu '{\"menu\":\"root\"}'")
}
Image {
id: icon
anchors.centerIn: parent
width: root.iconSize
height: root.iconSize
source: root.iconUrl
sourceSize.width: root.iconSize * 2
sourceSize.height: root.iconSize * 2
smooth: true
visible: root.iconReady
}
}
}
-9
View File
@@ -51,15 +51,6 @@
{
"id": "omarchy.monitor"
},
{
"id": "blob.cpu",
"type": "command",
"text": "󰍛",
"tooltip": "System monitor",
"onClick": "omarchy-launch-or-focus-tui btop",
"onMiddleClick": "alacritty",
"onRightClick": "ags toggle sys-monitor"
},
{
"id": "omarchy.power"
},
+72
View File
@@ -0,0 +1,72 @@
#!/bin/bash
STATE_FILE="$HOME/.local/state/omarchy/indicators/tablet-follow-focus"
SOCKET="$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock"
TABLET_DEVICE="${OMARCHY_TABLET_DEVICE:-hanvon-ugee-6-inch-pentablet-pen}"
PIDFILE="${XDG_RUNTIME_DIR:-/tmp}/omarchy-tablet-follow-focus.pid"
if [ "$(ps -o pgid= -p $$ | tr -d ' ')" != "$$" ]; then
exec setsid "$0" "$@"
fi
echo $$ >"$PIDFILE"
trap 'rm -f "$PIDFILE"' EXIT
selected_mode() {
local mode
[ -f "$STATE_FILE" ] || { echo all; return; }
mode=$(cat "$STATE_FILE" 2>/dev/null)
echo "${mode:-follow}"
}
focused_monitor() {
hyprctl monitors -j | jq -r '.[] | select(.focused==true) | .name'
}
monitor_exists() {
hyprctl monitors -j | jq -e --arg name "$1" 'any(.[]; .name == $name)' >/dev/null
}
map_to_output() {
hyprctl eval "hl.device({ name = \"$TABLET_DEVICE\", output = \"$1\" })" >/dev/null
}
apply_selected_mode() {
local mode target
mode=$(selected_mode)
case "$mode" in
all)
map_to_output ""
;;
follow)
target=$(focused_monitor)
[ -n "$target" ] && map_to_output "$target"
;;
*)
if monitor_exists "$mode"; then
map_to_output "$mode"
else
target=$(focused_monitor)
[ -n "$target" ] && map_to_output "$target"
fi
;;
esac
}
while true; do
apply_selected_mode
socat -U - UNIX-CONNECT:"$SOCKET" | while read -r line; do
case "$line" in
focusedmon\>\>*)
[ "$(selected_mode)" = "follow" ] && apply_selected_mode
;;
monitoradded\>\>*|monitorremoved\>\>*)
apply_selected_mode
;;
esac
done
sleep 1
done
+147
View File
@@ -0,0 +1,147 @@
#!/bin/bash
STATE_DIR="$HOME/.local/state/omarchy/indicators"
STATE_FILE="$STATE_DIR/tablet-follow-focus"
DAEMON="omarchy-tablet-follow-focus"
PIDFILE="${XDG_RUNTIME_DIR:-/tmp}/omarchy-tablet-follow-focus.pid"
TABLET_DEVICE="${OMARCHY_TABLET_DEVICE:-hanvon-ugee-6-inch-pentablet-pen}"
selected_mode() {
local mode
[ -f "$STATE_FILE" ] || { echo all; return; }
mode=$(cat "$STATE_FILE" 2>/dev/null)
echo "${mode:-follow}"
}
write_mode() {
mkdir -p "$STATE_DIR"
printf '%s\n' "$1" >"$STATE_FILE"
}
daemon_pid() {
[ -f "$PIDFILE" ] || return 1
local pid
pid=$(<"$PIDFILE")
[ -n "$pid" ] && kill -0 "$pid" 2>/dev/null && echo "$pid"
}
daemon_running() {
daemon_pid >/dev/null
}
start_daemon() {
daemon_running && return
setsid uwsm-app -- "$DAEMON" >/dev/null 2>&1 &
disown
}
stop_daemon() {
local pid
pid=$(daemon_pid) && kill -- "-$pid" 2>/dev/null
rm -f "$PIDFILE"
return 0
}
map_to_output() {
hyprctl eval "hl.device({ name = \"$TABLET_DEVICE\", output = \"$1\" })" >/dev/null
}
focused_monitor() {
hyprctl monitors -j | jq -r '.[] | select(.focused==true) | .name'
}
monitor_exists() {
hyprctl monitors -j | jq -e --arg name "$1" 'any(.[]; .name == $name)' >/dev/null
}
active_output() {
local mode
mode=$(selected_mode)
case "$mode" in
all) echo "" ;;
follow) focused_monitor ;;
*) echo "$mode" ;;
esac
}
describe_mode() {
case "$1" in
all) echo "Tablet spans all monitors" ;;
follow) echo "Tablet follows focused monitor" ;;
*) echo "Tablet locked to $1" ;;
esac
}
select_mode() {
local mode="$1"
case "$mode" in
all)
write_mode all
stop_daemon
map_to_output ""
;;
follow)
write_mode follow
start_daemon
;;
*)
if ! monitor_exists "$mode"; then
echo "Unknown monitor: $mode" >&2
exit 1
fi
write_mode "$mode"
start_daemon
map_to_output "$mode"
;;
esac
}
print_status() {
local mode enabled
mode=$(selected_mode)
if [ "$mode" = "all" ]; then enabled=false; else enabled=true; fi
jq -nc \
--argjson enabled "$enabled" \
--arg mode "$mode" \
--arg output "$(active_output)" \
--arg tooltip "$(describe_mode "$mode")" \
'{enabled: $enabled, mode: $mode, output: $output, class: (if $enabled then "enabled" else "disabled" end), tooltip: $tooltip}'
}
print_monitors() {
local mode
mode=$(selected_mode)
hyprctl monitors -j | jq -c --arg mode "$mode" \
'[.[] | {name, description, model, focused, selected: (.name == $mode)}]'
}
case "${1:-toggle}" in
toggle)
if [ "$(selected_mode)" = "all" ]; then select_mode follow; else select_mode all; fi
;;
on|enable)
select_mode follow
;;
off|disable)
select_mode all
;;
set)
[ -n "$2" ] || { echo "Usage: omarchy-toggle-tablet-follow set <monitor|follow|all>" >&2; exit 1; }
select_mode "$2"
;;
status|--status)
print_status
;;
monitors)
print_monitors
;;
autostart)
[ "$(selected_mode)" = "all" ] || start_daemon
;;
*)
echo "Usage: omarchy-toggle-tablet-follow [toggle|on|off|set <monitor|follow|all>|status|monitors|autostart]" >&2
exit 1
;;
esac