Merge quattro and route refreshes through omarchy-update-pacman
Upstream now runs every Omarchy-owned pacman transaction through the hidden omarchy-update-pacman helper so a mid-transaction systemd reexec cannot kill it. Keep the deferred pre-refresh-pacman hook and the command-scoped sudo wrapper, and call the helper from the refresh and channel commands; the wrapper still applies to the helper's own sudo. The sudo boundary fixture copies the helper into its root and runs a systemd-run stand-in that execs the wrapped pacman step in place. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
commit
4bd593f4ed
105 files changed
+2541
-361
No files matched your search
@@ -33,7 +33,7 @@ Recordings land in the configured Videos directory (override with
|
||||
`omarchy capture webcam resize <smaller|larger|reset|small|medium|large>`.
|
||||
|
||||
If recording fails to start, rerun with `OMARCHY_SCREENRECORD_DEBUG=true` to
|
||||
collect a log at `/tmp/omarchy-screenrecord.log` worth attaching to a bug
|
||||
collect a log at `$XDG_RUNTIME_DIR/omarchy-screenrecord.log` (or `${XDG_STATE_HOME:-$HOME/.local/state}/omarchy/omarchy-screenrecord.log` without a session runtime directory) worth attaching to a bug
|
||||
report.
|
||||
|
||||
## Text Capture (OCR)
|
||||
|
||||
@@ -38,7 +38,7 @@ drag-and-drop in the web form, so save the capture and hand the user the file
|
||||
path to attach (`gh` cannot upload media).
|
||||
|
||||
For screen-recording failures specifically, rerun with
|
||||
`OMARCHY_SCREENRECORD_DEBUG=true` and attach `/tmp/omarchy-screenrecord.log`.
|
||||
`OMARCHY_SCREENRECORD_DEBUG=true` and attach `$XDG_RUNTIME_DIR/omarchy-screenrecord.log` (or `${XDG_STATE_HOME:-$HOME/.local/state}/omarchy/omarchy-screenrecord.log` without a session runtime directory).
|
||||
|
||||
File the issue with `gh` when available:
|
||||
|
||||
|
||||
@@ -12,10 +12,11 @@ The private-use glyphs in `omarchy.ttf` are:
|
||||
- `U+E907` — Ollama, from <https://simpleicons.org/icons/ollama.svg>
|
||||
- `U+E908` — T3 Code, traced from the app icon in <https://aur.archlinux.org/cgit/aur.git/plain/t3code-icon.png?h=t3code-bin>, since upstream publishes no monochrome SVG
|
||||
- `U+E909` — Ori, from <https://openrouter.ai/brand/v2/openrouter-glyph-dark.svg>, OpenRouter's own mark: Ori ships no separate logo and its product page uses this one
|
||||
- `U+E90A` — Hermes, Font Awesome's staff-snake (CC BY 4.0) from <https://fontawesome.com/icons/staff-snake>, the mark Hermes serves as its favicon: their app icon is a portrait that reads as a smudge at menu size
|
||||
- `U+E90A` — Hermes, traced from the [official desktop app portrait](https://raw.githubusercontent.com/NousResearch/hermes-agent/2237be355906fbe6065ce1815711eee52b2d646e/apps/desktop/assets/icon.png), the same artwork shipped by `hermes-desktop`. The monochrome trace is kept in [hermes.svg](hermes.svg).
|
||||
- `U+E90B` — Perplexity, from <https://simpleicons.org/icons/perplexity.svg>
|
||||
- `U+E90C` — OpenClaw, traced from the lobster mascot the openclaw package ships as `dist/control-ui/favicon.svg`, since upstream publishes no monochrome SVG
|
||||
- `U+E90D` — Cursor, from <https://simpleicons.org/icons/cursor.svg>
|
||||
- `U+E90E` — Claude, from <https://simpleicons.org/icons/claude.svg>
|
||||
|
||||
The agent marks are monochrome so the menu can render them using the active
|
||||
theme's foreground and selection colors.
|
||||
@@ -0,0 +1,27 @@
|
||||
<!--
|
||||
Hermes portrait traced from apps/desktop/assets/icon.png at
|
||||
NousResearch/hermes-agent commit 2237be355906fbe6065ce1815711eee52b2d646e.
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Nous Research
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="13.951734482614546 14.280839829258241 100.90844360300241 98.9137748534075"><path d="M34.2 15C26.5 16.6 19.6 22.2 16.3 29.5C14.1 34.6 13 84.1 15 93.5C16.6 101.1 21 106.8 28.4 110.5C34.1 113.3 34.4 113.3 35.4 111.5C36 110.5 38.1 107.8 40.1 105.6C43.4 101.9 46 97.9 46 96.6C46 96.3 44.8 96.7 43.4 97.5C40.2 99.1 36.9 98.2 35.5 95.2C35 94 33.7 92.7 32.7 92.4C30.3 91.6 27 88 27 86.1C27.1 84.8 27.3 84.8 28.1 86.3C30.6 90.6 37.7 90.4 40.1 85.9L41.3 83.7L38.5 85.8C35 88.6 31.6 88.6 29 85.8C26.9 83.5 26.4 78.5 28 76C28.7 74.9 29 75.2 29 77.6C29 83.6 34.2 85.1 36.5 79.7C39.1 73.5 40.3 57.9 38.4 56.1C37.5 55.2 37 55.1 37.4 55.7C38.7 58 36 57.1 33.5 54.4C31.6 52.3 31 50.6 31 47.1C31.1 43.2 33.7 34.5 33.9 37.5C34 38.1 35.7 38.4 37.8 38.3C43.4 37.9 44 37.7 44 37C44 36.7 45.6 36.6 47.5 36.8C49.8 37.1 51 36.8 51 36C51 35.1 51.4 35.1 52.3 35.7C52.9 36.3 55.1 36.8 57.1 36.8C59.2 36.9 61.4 37.1 62.1 37.2C63 37.4 63.5 36.7 63.4 35.4C63.4 33.8 63.9 33.4 65.7 33.8C67 34 68 33.8 68 33.2C68 32.6 68.3 31.4 68.6 30.6C69.4 28.5 65.3 29 64 31C63.5 31.8 62.8 32.1 62.4 31.7C62 31.3 61.2 31.8 60.5 32.7C59.6 33.9 57.4 34.4 51.4 34.5C47.1 34.7 43.4 34.9 43.3 35C42.5 35.8 40.2 35.4 38.4 34.3C37.3 33.6 35.9 33.3 35.4 33.7C34.9 34.1 36 32.8 37.9 30.8C42.1 26.2 51.7 21 56 21C60.5 21 65.6 23.7 69 27.9C72.2 31.7 73 34.1 70.5 32C69.2 30.9 69.1 31 69.6 32.4C69.9 33.3 70.8 34 71.6 34C72.3 34 73.2 35.6 73.6 37.5C73.9 39.4 74.6 41.3 75.1 41.6C75.6 41.9 75.9 41.6 75.8 40.9C75.6 40.2 76.4 39.4 77.4 39.2C78.9 38.9 79.1 39.2 78.6 40.6C78.1 42 78.2 42.2 79 41.4C79.8 40.6 79.8 39.5 79 37.5C77.7 34.1 78.2 33.5 81 35C82.8 36 83.1 35.9 82.5 34.9C81.9 34 82.6 33.8 85.3 34.3C88.5 34.8 89 34.6 89.8 32.2C90.7 29.5 90.8 29.6 93 34.5C94.2 37.2 96.7 46.2 98.6 54.4C100.4 62.5 102.4 70.8 103 72.9C104.5 78.3 104.2 87.7 102.4 91.1C100.1 95.6 101.4 95.7 104.1 91.3C105.8 88.7 106.5 86 106.5 82.7V77.9L107.8 81.1C110.1 86.6 108.6 90.7 101.9 97.2C98.7 100.5 96 102.6 96 102.1C96 101.5 94.6 99.5 92.9 97.5C89.5 93.6 90 92.7 93.7 96.2C95.6 98 96 98.1 95.4 96.7C95.1 95.7 93.4 94.2 91.8 93.4C90.1 92.5 89.1 92.3 89.4 92.9C89.9 93.6 89.1 93.8 87.1 93.4C80.7 92.1 74.1 97.7 71.4 106.8C70.6 109.3 70 111.7 70 112.2C70 112.6 76 113 83.3 113C98 113 102 111.8 107.6 106.2C114.3 99.6 114.5 98.5 114.8 67.1C115.2 29.3 113.9 24.1 101.8 17.5L96.5 14.5L67.5 14.3C51.6 14.2 36.6 14.5 34.2 15ZM80.5 20.7C82.7 21.9 85.4 23.8 86.4 24.9C88.8 27.5 89.4 30.4 87.5 29.6C86.7 29.3 85.8 29.9 85.5 31C84.5 34.3 77.5 33.5 76 29.9C73.8 24.7 67.6 20 62.7 20C59.5 20 58.9 19.7 60 18.9C62.5 17.2 76 18.4 80.5 20.7ZM53 43.8C53 44.5 53.5 46.1 54.1 47.2C56 50.8 50.3 51.9 46.2 48.7C45.3 48 45.1 48.1 45.5 49.2C45.9 50.3 45.3 51 43.6 51.4C40.3 52.2 40.3 53.6 43.5 54.4C46.6 55.2 46.6 56 43.9 59.4C41.1 63 41.7 69.1 45.5 75.6L48.2 80.2L53.6 79.6C58.7 79 58.9 79.1 60.7 82C63.3 86.5 64.4 85.2 65.2 76.4C65.5 72.1 66.3 66.1 66.9 63.1C69.2 51.6 68.5 50 60.9 50C57.5 50 56.8 49.6 55.1 46.2C54 44.2 53.1 43.1 53 43.8ZM59.8 52.6C62 54.1 62 54.3 60.4 56.1C58.2 58.6 57.6 58.5 54.5 55.4C50.4 51.3 54.7 49 59.8 52.6ZM46.7 68.7C47.3 68.3 48.6 68.7 49.6 69.5C51.4 70.8 51.3 70.9 48.5 70.7C45.5 70.5 44 69.4 44 67.5C44 66.9 44.4 67.1 44.9 67.9C45.4 68.6 46.2 69 46.7 68.7ZM57.6 55.6C57.2 56.6 57.5 57 58.2 56.8C59.7 56.3 60.5 54 59.2 54C58.7 54 57.9 54.7 57.6 55.6ZM77.6 53.9C77.3 54.4 78.1 54.7 79.5 54.7C80.9 54.7 81.7 54.4 81.4 53.9C81.1 53.4 80.3 53 79.5 53C78.7 53 77.9 53.4 77.6 53.9ZM42 56.9C42 57.5 42.4 58.2 43 58.5C43.6 58.8 43.7 58.4 43.4 57.6C42.7 55.8 42 55.5 42 56.9ZM72.4 74.4C71.4 77.6 71.5 77.8 73 77C74.2 76.4 74.6 75.3 74.3 73.5C73.6 70.4 73.7 70.3 72.4 74.4ZM98.1 76.8C98 80.3 97.5 84.1 96.9 85.3C95.9 87.4 96 87.4 97.9 85.1C100.1 82.6 100.6 76.3 99 72.5C98.4 71.1 98.1 72.4 98.1 76.8ZM72 80.5C71 81.7 71 82.4 71.8 83.2C73.3 84.7 76.1 82.4 75.3 80.4C74.6 78.6 73.6 78.6 72 80.5ZM62.8 87.9C62 88.9 60.7 89.9 60 90.2C59 90.5 59.1 91.4 60.4 93.8C62.3 97.4 63 97.6 66.8 95.5C72.5 92.4 72.5 92.4 68.9 89.1C64.9 85.5 64.9 85.5 62.8 87.9ZM67 93.2C67 93.6 66.3 93.6 65.3 93.3C64.2 92.9 63.9 93 64.4 93.8C64.8 94.5 64.7 95 64.3 95C63.8 95 62.9 94 62.1 92.9C60.9 90.9 61 90.8 62.8 91.2C64.4 91.6 64.7 91.4 64.3 90.1C64 88.9 64.2 89 65.4 90.4C66.3 91.5 67 92.7 67 93.2ZM42.2 91C41.5 92.1 39.9 93 38.4 93C37 93 36.1 93.4 36.5 94C38.1 96.5 44.8 92.9 43.8 90C43.7 89.5 42.9 89.9 42.2 91ZM74 94C67.7 95.9 61.6 100.3 56.9 106.1C51 113.5 51.1 113 55.8 113C59.2 112.9 59.8 112.5 63.9 106.4C66.3 102.9 68.4 100.1 68.6 100.2C68.7 100.4 68 103.2 66.9 106.4C65.9 109.7 65.2 112.5 65.5 112.8C66.9 114.3 68.9 111.4 70 106.2C70.9 101.9 72.2 99.1 74.5 96.6C78 92.8 77.9 92.7 74 94ZM50.4 104.5C47.8 107.1 47.3 112 49.6 112.7C50.5 113 55 104.6 55 102.6C55 101.2 52.7 102.2 50.4 104.5ZM51.5 109C50.6 110.8 49.6 112 49.4 111.7C49.1 111.5 49.7 109.9 50.7 108.2C53 104.2 53.6 104.9 51.5 109ZM39.5 111C38.5 112.1 38.2 113 38.8 113C39.4 113 40.4 112.1 41 111C41.6 109.9 41.9 109 41.7 109C41.5 109 40.5 109.9 39.5 111Z"/></svg>
|
||||
Binary file not shown.
@@ -1 +1,3 @@
|
||||
o.window("^(1[p|P]assword)$", { no_screen_share = true, tag = "+floating-window" })
|
||||
-- 1Password 8.12 renamed its app id from "1Password" to the reverse-DNS form,
|
||||
-- so match both to keep older installs floating too.
|
||||
o.window("^(1[pP]assword|com\\.onepassword\\.OnePassword)$", { no_screen_share = true, tag = "+floating-window" })
|
||||
@@ -1,2 +1,2 @@
|
||||
-- Disable mouse focus (see https://github.com/basecamp/omarchy/pull/5183#issuecomment-4189299971).
|
||||
-- Disable mouse focus (see https://github.com/omacom/omarchy/pull/5183#issuecomment-4189299971).
|
||||
o.window("^(jetbrains-.*)$", { no_follow_mouse = true })
|
||||
+130
-22
@@ -5,6 +5,14 @@
|
||||
-- How much of the usable screen the console covers, measured from the top.
|
||||
local share = 0.5
|
||||
|
||||
-- A console holding a single window is boxed into a centered panel this many
|
||||
-- times wider than it is tall, rather than stretched the width of the screen.
|
||||
-- A second app on the scratchpad gets the full width back: two windows splitting
|
||||
-- a half-width column is worse than the band this replaced.
|
||||
local box = 2
|
||||
|
||||
local SCRATCHPAD = "special:scratchpad"
|
||||
|
||||
-- Seed the console with the default agent the first time it opens, rather than
|
||||
-- at boot, so nothing is running until it is wanted. The exec rule has to pin
|
||||
-- the workspace itself: Hyprland only tags a spawn with the workspace it came
|
||||
@@ -23,22 +31,25 @@ hl.config({
|
||||
},
|
||||
})
|
||||
|
||||
-- The panel is always flush with the top and always centered, so two numbers
|
||||
-- describe it: the gap down each side and the gap underneath.
|
||||
--
|
||||
-- Refitting replaces the rule in place rather than stacking a new one, but it
|
||||
-- still schedules a monitor and window state refresh, and monitor.focused fires
|
||||
-- on every hop between screens. Most of those hops do not change the number, so
|
||||
-- on every hop between screens. Most of those hops do not change the gaps, so
|
||||
-- only write the rule when it actually moves.
|
||||
local covering = nil
|
||||
local beside, below = nil, nil
|
||||
|
||||
local function cover(bottom)
|
||||
if covering == bottom then
|
||||
return
|
||||
local function cover(side, bottom)
|
||||
if beside == side and below == bottom then
|
||||
return false
|
||||
end
|
||||
covering = bottom
|
||||
beside, below = side, bottom
|
||||
|
||||
hl.workspace_rule({
|
||||
workspace = "special:scratchpad",
|
||||
workspace = SCRATCHPAD,
|
||||
gaps_in = 0,
|
||||
gaps_out = { top = 0, right = 0, bottom = bottom, left = 0 },
|
||||
gaps_out = { top = 0, right = side, bottom = bottom, left = side },
|
||||
|
||||
-- Nothing to highlight in a console that is only ever focused when it is
|
||||
-- open, and the active border reads as a stray frame around a panel that
|
||||
@@ -47,39 +58,136 @@ local function cover(bottom)
|
||||
|
||||
on_created_empty = seed,
|
||||
})
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- One window reads as a console and gets the panel. A second app has turned the
|
||||
-- scratchpad into a workspace, and a workspace wants the whole width. Only tiled
|
||||
-- windows count: the gaps are what size the panel, and a floating window on top
|
||||
-- of the console is not laid out by them.
|
||||
local function alone()
|
||||
local tiled = 0
|
||||
for _, window in ipairs(hl.get_workspace_windows(SCRATCHPAD)) do
|
||||
if not window.floating then
|
||||
tiled = tiled + 1
|
||||
end
|
||||
end
|
||||
return tiled <= 1
|
||||
end
|
||||
|
||||
-- Sizing the console with a window rule would freeze it at whatever the screen
|
||||
-- measured when it first opened, because Hyprland resolves those expressions
|
||||
-- once, as the window maps. Rescaling the monitor afterwards would leave a
|
||||
-- console that is no longer half of anything. Gaps are re-applied by the layout
|
||||
-- instead, so the console is sized by the gap left underneath it and that gap
|
||||
-- is recomputed whenever the monitor layout changes.
|
||||
local function fit()
|
||||
local monitor = hl.get_active_monitor()
|
||||
|
||||
-- instead, so the console is sized by the area left around it, and that area is
|
||||
-- recomputed whenever the monitor it opens on changes.
|
||||
local function fit(monitor)
|
||||
-- A monitor handle whose output has gone away answers nil to every field, and
|
||||
-- layout changes are exactly when that happens, so this also covers reading
|
||||
-- height and reserved below.
|
||||
-- width, height and reserved below.
|
||||
if not monitor or not monitor.scale or monitor.scale <= 0 then
|
||||
return
|
||||
return false
|
||||
end
|
||||
|
||||
-- Width and height are the panel's own pixels, so a monitor turned on its
|
||||
-- side still reports them the way the panel is built. The odd transforms are
|
||||
-- the quarter turns, and those are the ones that swap the work area.
|
||||
local width, height = monitor.width, monitor.height
|
||||
if monitor.transform % 2 == 1 then
|
||||
width, height = height, width
|
||||
end
|
||||
|
||||
-- Monitor dimensions are in physical pixels; gaps are logical, so the scale
|
||||
-- has to come out before the reserved area (already logical) comes off.
|
||||
local reserved = monitor.reserved
|
||||
local usable = monitor.height / monitor.scale - reserved.top - reserved.bottom
|
||||
height = height / monitor.scale - reserved.top - reserved.bottom
|
||||
width = width / monitor.scale - reserved.left - reserved.right
|
||||
|
||||
cover(math.max(0, math.floor(usable * (1 - share))))
|
||||
local tall = math.floor(height * share)
|
||||
local wide = width
|
||||
if alone() then
|
||||
wide = math.min(width, tall * box)
|
||||
end
|
||||
|
||||
return cover(math.floor((width - wide) / 2), math.floor(height - tall))
|
||||
end
|
||||
|
||||
-- The console keeps the geometry of the output it is open on: a follow_mouse hop
|
||||
-- onto another screen must not resize a console that is already showing. While
|
||||
-- it is hidden there is nothing to size but the output that will show it next.
|
||||
local function console_monitor()
|
||||
local ws = hl.get_workspace(SCRATCHPAD)
|
||||
local mon = ws and ws.visible and ws.monitor
|
||||
|
||||
if mon and mon.scale and mon.scale > 0 then
|
||||
return mon
|
||||
end
|
||||
|
||||
return hl.get_active_monitor()
|
||||
end
|
||||
|
||||
local function refit(monitor)
|
||||
if fit(monitor or console_monitor()) then
|
||||
-- Land the new gaps in this pass rather than a frame later, so the console
|
||||
-- does not visibly resize itself once it has already dropped down.
|
||||
hl.exec_scheduled_prop_refresh_immediately()
|
||||
end
|
||||
end
|
||||
|
||||
-- Until a monitor can be read, cover the whole work area rather than leaving
|
||||
-- the console unruled, so it is never seeded without its placement.
|
||||
cover(0)
|
||||
fit()
|
||||
-- the console unruled, so it is never seeded without its placement. A reload
|
||||
-- runs this again with the console already on screen, so it starts from the
|
||||
-- output the console is on rather than whichever one the pointer is over.
|
||||
cover(0, 0)
|
||||
fit(console_monitor())
|
||||
|
||||
hl.on("monitor.layout_changed", fit)
|
||||
hl.on("monitor.focused", fit)
|
||||
hl.on("monitor.layout_changed", function()
|
||||
refit()
|
||||
end)
|
||||
|
||||
hl.on("monitor.focused", function()
|
||||
refit()
|
||||
end)
|
||||
|
||||
-- Special workspaces open on the monitor they are toggled on, not on whichever
|
||||
-- output last happened to be focused when the rule was written, so these two
|
||||
-- take the monitor they are handed rather than looking one up.
|
||||
hl.on("workspace.special_active", function(ws, mon)
|
||||
if ws and ws.name == SCRATCHPAD then
|
||||
refit(mon)
|
||||
end
|
||||
end)
|
||||
|
||||
hl.on("workspace.move_to_monitor", function(ws, mon)
|
||||
if ws and ws.name == SCRATCHPAD then
|
||||
refit(mon)
|
||||
end
|
||||
end)
|
||||
|
||||
-- The panel is only centered while the console holds one tiled window, so the
|
||||
-- count has to be rechecked as apps come and go: opened and closed, moved on or
|
||||
-- off (Super+Alt+S, Super+Shift+1), and floated or tiled (Super+T, Super+O).
|
||||
-- window.close is left out, since it still counts the window on its way out and
|
||||
-- window.destroy follows it anyway. Measured on Hyprland 0.56.2, a move is
|
||||
-- trailed by several window.update_rules, as is a float toggle, and the last of
|
||||
-- those always reads the settled count; the earlier ones refit to what the rule
|
||||
-- already is, which cover() skips.
|
||||
--
|
||||
-- Only while it is on screen, though. A hidden console is refitted on its way in
|
||||
-- by workspace.special_active, and every window opened anywhere on the desktop
|
||||
-- would otherwise rewrite the rule.
|
||||
local function recount()
|
||||
local ws = hl.get_workspace(SCRATCHPAD)
|
||||
if ws and ws.visible then
|
||||
refit()
|
||||
end
|
||||
end
|
||||
|
||||
hl.on("window.open", recount)
|
||||
hl.on("window.destroy", recount)
|
||||
hl.on("window.move_to_workspace", recount)
|
||||
hl.on("window.update_rules", recount)
|
||||
|
||||
-- The direction names the edge the offset is measured from, not where the
|
||||
-- workspace goes: "slide top" drops it down into view, and "slide bottom"
|
||||
|
||||
@@ -241,6 +241,7 @@
|
||||
"install.terminal.ghostty": {"icon":"","label":"Ghostty","disabled":"omarchy-pkg-present ghostty","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-terminal ghostty'"},
|
||||
"install.terminal.kitty": {"icon":"","label":"Kitty","disabled":"omarchy-pkg-present kitty","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-terminal kitty'"},
|
||||
"install.ai.chatgpt": {"icon":"","iconFont":"omarchy","label":"ChatGPT Desktop","disabled":"omarchy-pkg-present openai-codex-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-chatgpt"},
|
||||
"install.ai.claude": {"icon":"","iconFont":"omarchy","label":"Claude Desktop","disabled":"omarchy-pkg-present claude-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-claude"},
|
||||
"install.ai.dictation": {"icon":"","label":"Dictation","disabled":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-install"},
|
||||
"install.ai.grok-bot": {"icon":"","iconFont":"omarchy","label":"Grok Bot","disabled":"omarchy-pkg-present grok-bot","action":"omarchy-install-and-launch 'Grok Bot' grok-bot grok-bot"},
|
||||
"install.ai.hermes": {"icon":"","iconFont":"omarchy","label":"Hermes Desktop","disabled":"omarchy-pkg-present hermes-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-hermes"},
|
||||
@@ -309,6 +310,7 @@
|
||||
"remove.service.dropbox": {"icon":"","label":"Dropbox","when":"omarchy-pkg-present dropbox","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-service-dropbox"},
|
||||
"remove.service.tailscale": {"icon":"","label":"Tailscale","when":"omarchy-pkg-present tailscale","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-service-tailscale"},
|
||||
"remove.ai.chatgpt": {"icon":"","iconFont":"omarchy","label":"ChatGPT Desktop","when":"omarchy-pkg-present openai-codex-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-chatgpt"},
|
||||
"remove.ai.claude": {"icon":"","iconFont":"omarchy","label":"Claude Desktop","when":"omarchy-pkg-present claude-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-claude"},
|
||||
"remove.ai.dictation": {"icon":"","label":"Dictation","when":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-remove"},
|
||||
"remove.ai.grok-bot": {"icon":"","iconFont":"omarchy","label":"Grok Bot","when":"omarchy-pkg-present grok-bot","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-grok-bot"},
|
||||
"remove.ai.lm-studio": {"icon":"","iconFont":"omarchy","label":"LM Studio","when":"omarchy-pkg-present lmstudio-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-lm-studio"},
|
||||
|
||||
@@ -5,8 +5,6 @@ Window.SetBackgroundBottomColor(0.101, 0.105, 0.149);
|
||||
|
||||
logo.image = Image("logo.png");
|
||||
logo.sprite = Sprite(logo.image);
|
||||
logo.sprite.SetX(Window.GetWidth() / 2 - logo.image.GetWidth() / 2);
|
||||
logo.sprite.SetY(Window.GetHeight() / 2 - logo.image.GetHeight() / 2);
|
||||
logo.sprite.SetOpacity(1);
|
||||
|
||||
# Use these to adjust the progress bar timing
|
||||
@@ -22,7 +20,18 @@ global.fake_progress_start_time = 0.0; # Track when fake progress started
|
||||
global.password_shown = 0; # Track if password dialog has been shown
|
||||
global.max_progress = 0.0; # Track the maximum progress reached to prevent backwards movement
|
||||
|
||||
# Window size the sprites were last laid out for
|
||||
global.layout_width = 0;
|
||||
global.layout_height = 0;
|
||||
|
||||
fun refresh_callback() {
|
||||
# Displays can show up after this script ran, e.g. a Thunderbolt monitor
|
||||
# whose DisplayPort tunnel comes up after plymouthd started. The window
|
||||
# grows to the largest display, so re-center everything when it changes.
|
||||
if (Window.GetWidth() != global.layout_width || Window.GetHeight() != global.layout_height) {
|
||||
layout();
|
||||
}
|
||||
|
||||
global.animation_frame++;
|
||||
|
||||
# Animate fake progress to limit over time with easing
|
||||
@@ -108,27 +117,23 @@ entry.image = Image("entry.png");
|
||||
bullet.image = Image("bullet.png");
|
||||
|
||||
entry.sprite = Sprite(entry.image);
|
||||
entry.x = Window.GetWidth() / 2 - entry.image.GetWidth() / 2;
|
||||
entry.y = logo.sprite.GetY() + logo.image.GetHeight() + 40;
|
||||
entry.sprite.SetPosition(entry.x, entry.y, 10001);
|
||||
entry.sprite.SetOpacity(0);
|
||||
|
||||
# Scale lock to be slightly shorter than entry field height
|
||||
# Original lock is 84x96, entry height determines scale
|
||||
lock_height = entry.image.GetHeight() * 0.8;
|
||||
lock_scale = lock_height / 96;
|
||||
lock_width = 84 * lock_scale;
|
||||
lock.height = entry.image.GetHeight() * 0.8;
|
||||
lock.width = 84 * (lock.height / 96);
|
||||
|
||||
scaled_lock = lock.image.Scale(lock_width, lock_height);
|
||||
lock.sprite = Sprite(scaled_lock);
|
||||
lock.x = entry.x - lock_width - 15;
|
||||
lock.y = entry.y + entry.image.GetHeight() / 2 - lock_height / 2;
|
||||
lock.sprite.SetPosition(lock.x, lock.y, 10001);
|
||||
lock.sprite = Sprite(lock.image.Scale(lock.width, lock.height));
|
||||
lock.sprite.SetOpacity(0);
|
||||
|
||||
# Bullet array
|
||||
bullet.sprites = [];
|
||||
|
||||
fun position_bullet(index) {
|
||||
bullet.sprites[index].SetPosition(entry.x + 20 + index * (7 + 5), entry.y + entry.image.GetHeight() / 2 - 3.5, 10002);
|
||||
}
|
||||
|
||||
fun display_normal_callback() {
|
||||
hide_password_dialog();
|
||||
|
||||
@@ -165,11 +170,8 @@ fun display_password_callback(prompt, bullets) {
|
||||
for (index = 0; index < bullets_to_show; index++) {
|
||||
if (!bullet.sprites[index]) {
|
||||
# Scale bullet image to 7x7 pixels
|
||||
scaled_bullet = bullet.image.Scale(7, 7);
|
||||
bullet.sprites[index] = Sprite(scaled_bullet);
|
||||
bullet.x = entry.x + 20 + index * (7 + 5);
|
||||
bullet.y = entry.y + entry.image.GetHeight() / 2 - 3.5;
|
||||
bullet.sprites[index].SetPosition(bullet.x, bullet.y, 10002);
|
||||
bullet.sprites[index] = Sprite(bullet.image.Scale(7, 7));
|
||||
position_bullet(index);
|
||||
}
|
||||
|
||||
bullet.sprites[index].SetOpacity(1);
|
||||
@@ -183,19 +185,11 @@ Plymouth.SetDisplayPasswordFunction(display_password_callback);
|
||||
|
||||
progress_box.image = Image("progress_box.png");
|
||||
progress_box.sprite = Sprite(progress_box.image);
|
||||
|
||||
progress_box.x = Window.GetWidth() / 2 - progress_box.image.GetWidth() / 2;
|
||||
progress_box.y = entry.y + entry.image.GetHeight() / 2 - progress_box.image.GetHeight() / 2;
|
||||
progress_box.sprite.SetPosition(progress_box.x, progress_box.y, 0);
|
||||
progress_box.sprite.SetOpacity(0);
|
||||
|
||||
progress_bar.original_image = Image("progress_bar.png");
|
||||
progress_bar.sprite = Sprite();
|
||||
progress_bar.image = progress_bar.original_image.Scale(1, progress_bar.original_image.GetHeight());
|
||||
|
||||
progress_bar.x = Window.GetWidth() / 2 - progress_bar.original_image.GetWidth() / 2;
|
||||
progress_bar.y = progress_box.y + (progress_box.image.GetHeight() - progress_bar.original_image.GetHeight()) / 2;
|
||||
progress_bar.sprite.SetPosition(progress_bar.x, progress_bar.y, 1);
|
||||
progress_bar.sprite.SetOpacity(0);
|
||||
|
||||
fun progress_callback(duration, progress) {
|
||||
@@ -216,6 +210,39 @@ fun progress_callback(duration, progress) {
|
||||
|
||||
Plymouth.SetBootProgressFunction(progress_callback);
|
||||
|
||||
#----------------------------------------- Layout --------------------------------
|
||||
|
||||
# Center the logo, dialogue and progress bar for the current window size
|
||||
fun layout() {
|
||||
global.layout_width = Window.GetWidth();
|
||||
global.layout_height = Window.GetHeight();
|
||||
|
||||
logo.sprite.SetX(global.layout_width / 2 - logo.image.GetWidth() / 2);
|
||||
logo.sprite.SetY(global.layout_height / 2 - logo.image.GetHeight() / 2);
|
||||
|
||||
entry.x = global.layout_width / 2 - entry.image.GetWidth() / 2;
|
||||
entry.y = logo.sprite.GetY() + logo.image.GetHeight() + 40;
|
||||
entry.sprite.SetPosition(entry.x, entry.y, 10001);
|
||||
|
||||
lock.x = entry.x - lock.width - 15;
|
||||
lock.y = entry.y + entry.image.GetHeight() / 2 - lock.height / 2;
|
||||
lock.sprite.SetPosition(lock.x, lock.y, 10001);
|
||||
|
||||
for (index = 0; bullet.sprites[index]; index++) {
|
||||
position_bullet(index);
|
||||
}
|
||||
|
||||
progress_box.x = global.layout_width / 2 - progress_box.image.GetWidth() / 2;
|
||||
progress_box.y = entry.y + entry.image.GetHeight() / 2 - progress_box.image.GetHeight() / 2;
|
||||
progress_box.sprite.SetPosition(progress_box.x, progress_box.y, 0);
|
||||
|
||||
progress_bar.x = global.layout_width / 2 - progress_bar.original_image.GetWidth() / 2;
|
||||
progress_bar.y = progress_box.y + (progress_box.image.GetHeight() - progress_bar.original_image.GetHeight()) / 2;
|
||||
progress_bar.sprite.SetPosition(progress_bar.x, progress_bar.y, 1);
|
||||
}
|
||||
|
||||
layout();
|
||||
|
||||
#----------------------------------------- Message --------------------------------
|
||||
|
||||
message_sprite = Sprite();
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Create the Cam Link 4K virtual camera
|
||||
After=systemd-modules-load.service
|
||||
|
||||
[Service]
|
||||
# Reruns on every relay start, so a deleted or unloaded device comes back.
|
||||
Type=oneshot
|
||||
ExecStart=/usr/bin/modprobe v4l2loopback
|
||||
ExecStart=/bin/sh -c 'grep -qsx "Cam Link 4K" /sys/devices/virtual/video4linux/*/name || exec v4l2loopback-ctl add -n "Cam Link 4K" -x 1'
|
||||
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
# udev starts this when the Cam Link 4K appears. Only the device's stop is
|
||||
# tied in: a start dependency on it would leave a job waiting whenever the
|
||||
# base v4l2-relayd.service is started with no Cam Link attached.
|
||||
ConditionPathExists=/dev/camlink4k
|
||||
StopPropagatedFrom=dev-camlink4k.device
|
||||
Requires=camlink-4k-loopback.service
|
||||
After=camlink-4k-loopback.service
|
||||
|
||||
[Service]
|
||||
RestartSec=2
|
||||
# The packaged command line, plus sync=false on the sink. v4l2src stamps each
|
||||
# frame with its capture time, so by the time it reaches the sink it is already
|
||||
# past due and a synced sink drops every one of them.
|
||||
ExecStart=
|
||||
ExecStart=/bin/sh -c 'DEVICE=$(grep -l -m1 -E "^${CARD_LABEL}$" /sys/devices/virtual/video4linux/*/name | cut -d/ -f6); exec /usr/bin/v4l2-relayd -i "${VIDEOSRC}" $${SPLASHSRC:+-s "$${SPLASHSRC}"} -o "appsrc name=appsrc caps=video/x-raw,format=${FORMAT},width=${WIDTH},height=${HEIGHT},framerate=${FRAMERATE} ! videoconvert ! v4l2sink name=v4l2sink sync=false device=/dev/$${DEVICE}" $EXTRA_OPTS'
|
||||
@@ -1,6 +1,6 @@
|
||||
[Unit]
|
||||
Description=Omarchy speaker tuning filter-chain
|
||||
Documentation=https://github.com/basecamp/omarchy/blob/master/docs/audio-tuning.md
|
||||
Documentation=https://github.com/omacom/omarchy/blob/master/docs/audio-tuning.md
|
||||
# WirePlumber does the linking, so starting before it is up risks the output being
|
||||
# linked before the speaker device has been discovered.
|
||||
After=pipewire.service wireplumber.service
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Keep the Elgato Cam Link 4K's raw capture node away from users and hand it to
|
||||
# the v4l2-relayd instance that re-exposes it as a 16:9 virtual camera.
|
||||
# Sorted after 70-uaccess adds the tag and before 73-seat-late applies its ACL.
|
||||
SUBSYSTEM=="video4linux", ENV{ID_VENDOR_ID}=="0fd9", ENV{ID_MODEL}=="Cam_Link_4K", ENV{ID_V4L_CAPABILITIES}==":capture:", \
|
||||
TAG-="uaccess", OWNER="root", GROUP="root", MODE="0600", SYMLINK+="camlink4k", \
|
||||
TAG+="systemd", ENV{SYSTEMD_ALIAS}="/dev/camlink4k", ENV{SYSTEMD_WANTS}="v4l2-relayd@camlink.service"
|
||||
@@ -0,0 +1,9 @@
|
||||
# Elgato Cam Link 4K relayed as a fixed 16:9 virtual camera. Browser meeting
|
||||
# apps send 720p at most; raise WIDTH/HEIGHT (up to 3840x2160) for apps that
|
||||
# can use more.
|
||||
VIDEOSRC="v4l2src device=/dev/camlink4k"
|
||||
FORMAT=NV12
|
||||
WIDTH=1280
|
||||
HEIGHT=720
|
||||
FRAMERATE=30/1
|
||||
CARD_LABEL="Cam Link 4K"
|
||||
Reference in new issue
Block a user