Commit Graph
40 Commits
Author SHA1 Message Date
David Heinemeier HanssonandGitHub 2cc3510d2a Offer an AI diagnosis when a process crashes (#6746)
* Offer an AI diagnosis when a process crashes

systemd-coredump journals every core dump under a known MESSAGE_ID with the
crashing program, pid, and signal as structured fields. omarchy-crash-watch
follows that stream and raises a "Process crashed: <program>" toast; clicking it
opens omarchy-agent-crash, which briefs the default agent on the crash.

The toast goes through omarchy-notification-send --exec rather than a libnotify
action, because the shell runs clicks from its own omarchy-exec hint and never
emits ActionInvoked. It keeps the default "omarchy-action" app name too, the
only one shouldBypassDnd() lets through -- a crash being the last notification
worth swallowing. It stays quiet until an agent is configured, since a
diagnosis is all it offers.

The method lives in a diagnose-crash skill rather than the prompt, so it is
edited in one place and works with whichever agent is default. It covers
investigating the core, and reporting a confirmed Omarchy bug upstream: scoped
to bugs Omarchy controls, searched for duplicates first, only with the user's
agreement, and signed with the model and harness that produced it.

A migration reaches existing installs, whose skill symlinks and unit enablement
would otherwise sit behind one-time setup paths.

* Let the diagnosis clean up the core it extracted

"Do not modify or delete anything" contradicted the symbolization step right
above it, which writes a core to a temp file and deletes it on exit. Read
literally, the core survives -- and the same section warns it holds passwords
and tokens. The prohibition is about the system, not about your own scratch.

* Do not spend a crash toast on a dead notification server

The shell owns org.freedesktop.Notifications, so its own crash takes the
notification server down with it -- and a shell crash is exactly what you want
told about. The toast was sent once into that gap and the dedupe window was
recorded regardless, so the rest of the crash loop went quiet for a minute and
`journalctl -n 0` never replays what was missed.

It now waits for the restarted shell to reclaim the bus name, as
omarchy-migrate-notify already does, and only a delivered toast starts the
dedupe window.
2026-08-12 18:37:40 +02:00
David Heinemeier HanssonandClaude Opus 5 8be0ca9d43 Expect Tailscale on the right in the bar defaults test
99293aa gave the Tailscale widget a right defaultSection, but the test
still asserted it landed in center after the weather widget, and that
dropbox was the first widget after the tray.

Anchor both assertions on the tray so they track the placement contract
in omarchy-bar rather than hardcoded indexes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 04:22:01 -07:00
David Heinemeier HanssonandGitHub 57ea0b4cd0 Simplify bar plugin management (#6435) 2026-07-29 22:39:55 -04:00
09b955dc75 Manage plugins from Setup > Plugins (#6420)
* Give built-in plugins an honest on/off state

Every built-in reported itself enabled no matter what. A bar widget said
"enabled" while sitting nowhere near the bar, and disabling a built-in service
silently did nothing, because enabled meant "listed in plugins[]" and a
built-in never is. Nothing surfaced that, since the only caller listing plugins
was the CLI.

For a widget, on and off is its place in the bar, so listPlugins reports layout
membership -- what enable/disable actually toggles. For everything else built
in, loading by default is the right behaviour to keep, so switching one off is
recorded the other way round, in disabledPlugins[]. shell.json still carries
only the deviation from the defaults: the key is dropped the moment nothing is
switched off, leaving a config that never disabled anything byte-identical.

isEnabled still answers a separate question -- whether the component loads at
all -- and deliberately does not follow a widget out of the bar. omarchy.menu
is both a widget and the menu itself, so tying the two together would let
taking its button off the bar lock the menu out of the shell, with no way back
that isn't the CLI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Manage plugins from Setup > Plugins

Plugins were CLI-only. Setup > Plugins now offers Enable, Disable, Add, and
Remove, each list living in the menu itself so picking a row acts on it.

Enable and Disable cover the built-ins as well as anything installed -- the bar
widgets you can put in the bar, the services and overlays you can switch off.
Remove is limited to plugins the user installed, since a built-in has no
checkout to delete, and stays hidden until there is one. Whole-bar
replacements are left out; those are chosen under Style.

Enabling a bar widget asks for a section first, because enabling alone drops it
on the right and the only way to move it was a follow-up bar plugin move. The
CLI asks the same question after its own add, so both paths place a widget the
same way. Add and Remove run in a terminal: one needs a git URL and shows the
trust warning before cloning, the other deletes a checkout and prints where it
backed it up.

Providers grew two hooks for this. placementFor turns a row into a submenu
instead of an action, and volatile re-runs the enumeration when its submenu is
entered -- picking from these lists is what changes them, and rows a provider
no longer returns now drop out instead of lingering forever.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Trim the plugin menu after review

Menu.qml carried its own shellQuote while already importing Util and calling
Util.shellQuote a few lines up; two copies of the same escaping is one place
for a future fix to miss. isDisabled walked the array by hand to compare values
it writes itself, and dropDisabled was an eight-line helper with one caller.

Two bugs came out of the same pass. A whole-bar replacement belongs under Style
rather than these lists, but the exclusion sat in the shared row builder, so a
third-party bar could be installed and never removed -- Remove would show an
empty list under a guard that said something was there. The exclusion now sits
on the two lists that mean it.

Rows are keyed by id, and distinct plugin ids can slugify alike: acme.foo,
acme_foo and acme-foo all give acme-foo. The merge keeps the first row per id,
so the rest simply vanished from the list with nothing to say why. Row ids are
now made distinct before merging.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Pick a plugin the way we pick a theme

Setup > Plugins listed plugins as menu rows, which needed three providers, a
placement submenu, a volatile-refresh hook and a row-swap in the merge. Only
Font and Apps are built that way. Theme, Background, Unlock, Timezone and
Keybindings all pipe a list into omarchy-menu-select instead, which is one
action string and a small script -- so that is what these use now.

The trade is search: a plugin name is no longer findable from the root prompt.
Neither is a theme name or a timezone, and Enable Plugin still is, so the loss
sits where the rest of the menu already puts it.

Two pieces of the row machinery stay, because they are worth having for the
lists that remain. A volatile provider re-runs when its submenu is entered, so
a font installed since the shell started now shows up without restarting it,
and rows a provider stops returning drop out. Row ids are still made distinct
before merging: Fira Code and Fira-Code both slug to fira-code, and a repeated
id was silently dropped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Let a picked option carry an icon

Moving the plugin lists onto omarchy-menu-select cost them their glyphs: the
select mode has always hardcoded an empty icon, which is why Timezone and
Keybindings have none either. An option may now lead with one, as
"<glyph><TAB><label>". The menu shows the glyph, filters on the label, and
hands the label back, so a caller never strips a glyph off its own selection
and a list of plain strings behaves exactly as before.

The plugin picker uses it for the puzzle glyph on each plugin and the align
glyphs on the sections, which also regain the capitals they lost when the
section names were passed through raw.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Switch bars by enabling one

A bar option was kept out of Enable and Disable on the grounds that picking
which bar to run belongs under Style -- but nothing under Style ever offered
it, so an installed bar could be added and removed and never actually put to
use. The menu was guarding a door to a room that was never built.

Enabling one is the switch. setEnabled already assigns bar.id for a bar
option, so a bar has always replaced the one before it; only the picker's
filter stood in the way. Dropping it costs nothing else, because enabled for a
bar option means active: the bar in use is the one row absent from Enable,
every other installed bar is one pick away, and the built-in is just another
entry, so going back to it is enabling Bar.

Disable keeps the exclusion. That is the one verb a bar cannot answer -- there
is no off, only a successor -- and offering it would have listed the built-in
bar on a stock system, where turning it off deletes a bar.id that was never
set and nothing happens.

A bar carries the bar glyph rather than the puzzle one, so a row that replaces
the whole bar does not read like one more widget to switch on, and enable now
says "Now using X as the bar" instead of "Enabled X", which understated a
whole-bar swap in both the enable and the freshly-added path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Refuse a plugin that declares a kind it cannot load

A kind is a promise to supply something to load, and the shell reads that
something from a fixed key: entryPoints.bar to draw a bar, entryPoints.menu to
open a menu. Nothing checked the promise. A manifest could claim kinds ["bar"]
with no bar entry point, pass validation, install, and enable -- and then the
bar would fall back to the built-in and the widget would be skipped, leaving a
plugin that does nothing, explained only by a console.warn nobody reads.

Our own plugins have been held to this table by plugins-test.sh all along.
This holds third-party ones to the same table, at add and update time, where
there is still someone to tell.

A kind outside the table is left alone rather than guessed at, so a shell that
learns a new kind does not need this list updated first. The cost is that a
misspelled kind still installs quietly.

omarchy-plugin-validate had no tests; it has some now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Act on the plugin whose row was picked

The picker showed a name and then looked that name up again across every
plugin, filtered set or not, taking the first match. Two plugins can share a
name: cloning one keeps the name it was cloned from, so the documented
`omarchy plugin clone omarchy.clock local.clock` leaves two plugins called
Clock. Enable listed the clone -- the built-in was already enabled, so only the
clone was eligible -- and then enabled omarchy.clock, moving the built-in
widget instead. Remove listed the clone and tried to delete a built-in that has
no checkout to delete.

A row now carries its id alongside its label, and the id is read back off the
row that was picked instead of being derived from the name a second time. Where
a name is not unique among the rows on offer, the label carries the id too, so
two rows that would both say Clock can be told apart at all -- which they could
not before, whichever one the pick resolved to.

The verb prompt only ever sees the first two fields, so the menu shows what it
always did.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Never ask a bar where to sit in the bar

A manifest may declare both bar and bar-widget, and validation accepts it. The
picker saw bar-widget, asked for a section, and passed it to enable. setEnabled
takes bar as the dominant kind: it writes bar.id and returns, adding nothing to
any layout, so the move that followed had no widget to find and failed -- after
the bar had already been switched. A partial success with an error on the way
out.

Bar wins ahead of bar-widget now, in the picker and in the placement prompt
`plugin add --enable` asks, so a bar is enabled without a placement it cannot
use. The CLI refuses a placement on a bar outright, before the bar is switched
rather than after, since `omarchy plugin enable <bar> --section left` could
reach the same half-applied state without going through either.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Only replacement, no off

* Add default placement for bar widgets

* Simplify plugin menu actions

* Document plugin placement behavior

* Allow dropping widgets in empty bar space

* Treat plugin dependencies as runtime invariants

* Reject duplicate plugin ids on add

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 18:11:32 -04:00
David Heinemeier HanssonandClaude Opus 5 6e07fd0e82 Supervise fcitx5 so XCompose sequences can't silently die
fcitx5 is what turns the CapsLock compose sequences in ~/.XCompose into
text for Wayland clients -- CapsLock m s for an emoji, CapsLock space n
for your name. It was launched fire-and-forget from Hyprland's autostart
via uwsm-app, so nothing supervised it, nothing restarted it, and it
logged nowhere. When it went away, every compose sequence stopped
working for the rest of the session with no visible symptom beyond
"emoji input is busted", and no record of why.

That is not hypothetical: it was found dead on a running machine with no
coredump, no OOM kill, and nothing in the journal to explain it.

Move it to a systemd user service:

- Restart=always, not on-failure. fcitx5 exits 0 when it finds another
  instance already owning org.fcitx.Fcitx5, and a clean exit still
  leaves the user with no input method.
- After=/PartOf=graphical-session.target. It needs WAYLAND_DISPLAY and
  DISPLAY, which uwsm imports into the user manager before reaching the
  target, and its wayland connection dies with the compositor.
- ConditionEnvironment=WAYLAND_DISPLAY. After= is ordering only and does
  not stop the unit from being started while the target is inactive. An
  update over SSH has a live user manager (pam_systemd) and no graphical
  session, and a fcitx5 started there comes up blind -- then stays
  active, so the later target activation won't pull in a working one,
  because Wants= does not restart what is already running. Skipping the
  start leaves the unit enabled and healthy for the next graphical login.

The migration hands over inside a live session only: it enables without
--now, and only when graphical-session.target is active does it drop the
autostart-launched process and start the unit. Because that kills a
fcitx5 that was working a moment ago, a failed start is reported instead
of leaving the session mute with the migration marked complete.

omarchy-restart-xcompose now drives the unit. It still clears any fcitx5
running outside it first: that process owns the bus name, so the unit's
instance would exit on arrival and the stale one would keep serving the
old table -- a restart that reports success and changes nothing.

Side benefit: fcitx5 now logs to the journal under its own unit, so the
next disappearance leaves a record.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 09:49:15 -07:00
e57f3b286c Send and receive files with Taildrop (#6375)
* Send files to a tailnet machine with Taildrop

The panel gets a send button next to the copy one on every machine that
Tailscale grades as a Taildrop target, and `s` does the same from the
keyboard. Picking runs through the XDG portal chooser, so it looks like
the file dialog every other app opens.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJQJfHXXApUk6En8EZisHg

* Save incoming Taildrop files and say so

Linux keeps Taildrop files in the daemon's inbox until someone asks for
them, so nothing arrived until you ran `tailscale file get` by hand. A
user service now stages each delivery next to the downloads directory,
hands it over under a free name, and announces it — with a preview when
it's an image, and a click to open it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJQJfHXXApUk6En8EZisHg

* Float every portal dialog, not just the titled ones

The portal only ever shows dialogs, and the title regex missed any
chooser an app names something else — ours included.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJQJfHXXApUk6En8EZisHg

* Re-run the Taildrop enable now that the unit ships

The unit was never installed to /usr/lib/systemd/user/, so the enable had
nothing to act on and machines that already ran the migration carry a marker
for a no-op. Rename it so they get a working pass, and report what systemctl
says instead of a bare failure line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Wait for the file chooser on the connection that asked for it

The portal answers a request with a Response signal directed at the connection
that made it, and dbus-daemon delivers directed signals only to that
connection. gdbus monitor registers with AddMatch rather than BecomeMonitor, so
it never saw the reply: every pick left omarchy-file-select blocked on a read
that could not arrive, taking omarchy-tailscale-send down with it before it
reached either its notification or the transfer.

Make the call and wait for the signal on one connection, and give up after ten
minutes so an unanswered dialog cannot strand the caller.

Drop the "Sending to" notification while here, so a send reports once.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Mark Taildrop notifications with the panel's send glyph

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Stop a hung tailscale poll from freezing the panel

Each poll is skipped while its own process is still running, so one that never
exits leaves the panel showing whatever it last read, for good: the peer list
keeps a woken machine missing, and opening the panel cannot help because open
runs the same refresh that hits the same guard.

Reap anything still running fifteen seconds after a refresh, well inside the
thirty second interval, so the next tick starts clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Place a moved widget where an added one lands

'move omarchy.media left' named a section, not a slot, but the section went
through as an explicit target, which resolves a missing index by appending. The
widget landed on the far end of the row instead of after the section anchor
where 'add' puts it.

Its test has never run: the assertion covering this went in four hours after an
unrelated layout change had already stopped the file, and the runner stops the
whole suite at the first failure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Keep the config test from failing on things it is not about

The center layout assertion pinned the whole row, so parking the indicators
left of the clock broke a test named for update sitting next to weather. Assert
that adjacency instead.

The package-defaults check reads PKGBUILDs from the omarchy-pkgs repo and blew
up with a traceback wherever that is not a sibling checkout. Skip it when the
checkout is absent, honour OMARCHY_PKGS_ROOT when it is somewhere else, and
keep failing when it is present and wrong.

Between them these stopped the suite eighty files early.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Make the file chooser a Python command rather than a bash host for one

The portal work was a heredoc wedged inside a bash script that existed only to
parse two flags. Drop the host: argparse covers the flags, and the file says at
the top why it is the one command here not written in bash.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Tell a chooser that never opened apart from one that was dismissed

Three fixes from review:

The poll watchdog rearmed on every refresh, so a refresh interval shorter than
its timeout — the setting goes down to five seconds — pushed the deadline ahead
of a hung process forever. Arm it on the launch that needs watching and leave
it alone.

omarchy-file-select exited 1 both for nothing picked and for a chooser that
could not run, and omarchy-tailscale-send read it through a process
substitution, which drops the status anyway. A session bus that was not there
looked exactly like someone changing their mind. Separate the two exits and
read them with a command substitution.

Delivery picked a free name and then renamed, which overwrites anything that
takes the name in between. Link to the name instead: link(2) refuses one that
is taken, so the check and the claim are the same step.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 19:07:11 -07:00
David Heinemeier HanssonandClaude Opus 5 f375113fe3 Find the omarchy-pkgs checkout for PKGBUILD coverage
The three hardcoded candidates are all relative to a sibling of the omarchy
checkout, so a clone anywhere else, such as ~/Work/omacom/omarchy-pkgs, found
nothing. The block then crashed on read_text() and the whole test exited on an
unhandled traceback, which reads as an unrelated failure rather than a missing
checkout.

Add the omacom layouts, an OMARCHY_PKGS_PATH override taking either the
checkout or its pkgbuilds directory, and a named failure listing where it
looked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 14:53:29 -07:00
David Heinemeier HanssonandClaude Opus 5 425c3ff84d Only check for pending migrations at login
omarchy-update-user-notify.path watched /usr/share/omarchy/migrations, but
pacman writes that directory during every update, including the blessed
omarchy update, which runs omarchy-migrate a step later. The watcher fired a
critical notification for the migrations the update was already applying in
the visible terminal. A watcher cannot tell that apart from a bypassed
pacman -Syu, so the only trigger that never collides with a running update is
a once-per-login check.

The service that already ran at graphical-session.target is now the whole
mechanism, renamed after the command it runs. That is also all the second-user
case needs: markers are per-user, so anyone who did not run the update finds
them missing at their next login.

Login timing means the toast can be sent before the shell has claimed
org.freedesktop.Notifications, so the notifier waits for a live server first.
The wait is omarchy-first-run's, lifted into omarchy-notification-wait rather
than duplicated.

The package keeps omarchy-update-user-notify.service as a symlink onto the new
unit. Existing users hold an absolute wants symlink to the old path, and the
migration that repoints it only runs for users who run an update, which is the
opposite of who the notifier is for.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 14:53:29 -07:00
David Heinemeier HanssonandClaude Opus 5 2e04f093c1 Ship zram tuning as a vendor drop-in
pacman checks file conflicts during transaction prepare, so the --overwrite
entry added alongside /etc/systemd/zram-generator.conf never got to run: the
copy of omarchy-update-system-pkgs driving the upgrade is the one already on
disk. Every machine carrying archinstall's copy aborted the transaction.

zram-generator.conf(5) reserves /etc for the local admin and has vendors ship
snippets under /usr/lib/systemd/zram-generator.conf.d/, where drop-ins outrank
the main config file. Nothing collides, and the tuning wins whether or not
archinstall's file is still around.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LjeyQZsNBxqyYy9z8KaKm7
2026-07-25 14:01:38 -07:00
David Heinemeier Hansson 57923f0378 Support positional section when moving bar widgets 2026-07-25 11:14:04 -07:00
Kyunghyun Park 876d6bcd23 Fix bar plugin JSON value validation 2026-07-25 01:33:30 +09:00
David Heinemeier HanssonandClaude Fable 5 c70ae3a405 Group bar position under Style > Menu Bar and add transparency toggle
omarchy bar transparent now accepts toggle, flipping the current value
in one jq update (absent counts as opaque, matching the shell).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 16:47:36 -07:00
David Heinemeier Hansson 182330465a Simplify omarchy-bar into a bar-settings and bar-plugin split
The omarchy-bar command had grown three overlapping ways to inspect the
bar (show/layout/list/options, plus selected/active/available/widgets
aliases) and mixed layout mutation in with bar-level settings. Untangle
it into two focused commands:

- omarchy-bar keeps only the bar-level settings that write shell.json:
  use, reset, position, transparent, and settings. reset now delegates
  to `use omarchy.bar` rather than duplicating the del(.bar.id) write.
- omarchy-bar-plugin owns all layout mutation: add, move, remove, set,
  and replace, with the placement flags and jq resolve/anchor helpers.
  `omarchy bar plugin ...` routes here via the dispatcher.

Drop the inspection commands entirely: the layout is visible on the bar,
the config is shell.json, and widget/option ids come from
`omarchy plugin list`. Nothing consumed the show output programmatically
except tests. This also removes omarchy-bar-position, whose jq write was
a duplicate of `omarchy bar position`.

Strip environment-invariant guards (require_command, require_omarchy_path)
that defended against jq or OMARCHY_PATH being absent — neither happens on
a real system. Keep the user-input validation (--section/--index) and the
atomic shell.json write.

Update callers (service install/remove, refresh-shell, plugin-clone,
plugin enable), keybindings, the menu, tests, and docs to the new split.
2026-07-17 16:28:17 -07:00
David Heinemeier Hansson 93181b6a22 Restore service widgets with bar defaults 2026-07-16 21:15:00 -07:00
David Heinemeier Hansson 055ffaccd4 Split omarchy-plugin into siblings and consolidate bar config into omarchy-bar
omarchy-plugin mixed plugin lifecycle, clone/edit, and bar-layout mutation.
The bar layout was mutated by two separate engines (an embedded Python
mutate_bar here and jq in omarchy-config-shell-bar), each with its own
manifest walker.

- omarchy-plugin is now a ~220-line router; clone and edit move to
  omarchy-plugin-clone and omarchy-plugin-edit, matching the existing
  plugin-{source,add,update,remove,available,validate,scan} sibling pattern
- omarchy-config-shell-bar is replaced by omarchy-bar, the single bar engine
  (show/layout/list/options/use/reset/add/move/remove/set/replace/position/
  transparent/settings) with one jq pipeline; the Python mutate_bar and its
  python3 dependency are gone
- omarchy-plugin-catalog is the shared manifest scanner used by omarchy-bar
  (widget/option enumeration, add validation) and omarchy-plugin-clone
  (source enumeration), replacing three find|jq re-implementations
- service install/remove and refresh-shell call omarchy-bar; docs and tests
  updated to the new command surface
2026-07-02 11:10:24 -07:00
David Heinemeier Hansson 1db2323fd9 Remove legacy bar widgets 2026-06-23 16:22:47 +02:00
David Heinemeier Hansson b30758dd2d Add optional service widgets on shell refresh 2026-06-23 16:22:27 +02:00
David Heinemeier Hansson 428b9c7b29 Use omarchy-upgrade-to-quattro 2026-06-21 12:31:06 +02:00
Ryan Hughes c048b2c745 Normalize Snapper snapshot setup 2026-06-21 02:25:42 -04:00
David Heinemeier Hansson c62108fa0b Fix current theme state migration 2026-06-20 14:36:27 +02:00
Ryan Hughes c582e7133a Unify Omarchy migrations 2026-06-12 13:49:46 -04:00
Ryan Hughes 0f5e81143e Move current theme state to local state 2026-06-09 19:48:37 -04:00
Ryan Hughes f79a231cc7 Pause Hyprland reloads during settings updates 2026-06-09 14:20:36 -04:00
Ryan Hughes 64581ec289 Remove hot link / unlink bits 2026-06-08 12:03:40 -04:00
Ryan Hughes fc6feedae5 Load Omarchy fontconfig after generic aliases 2026-06-06 19:31:51 -04:00
Ryan Hughes 2c44fdf5f3 Merge remote-tracking branch 'origin/omarchy-shell' into omarchy-4 2026-06-05 18:35:34 -04:00
Ryan Hughes 1409f85745 Make the bar more easily swapable 2026-06-05 16:49:30 -04:00
Ryan Hughes 92de15214c Reconcile omarchy-shell rebase 2026-06-04 18:40:57 -04:00
Ryan Hughes d0cac7346a Finish v4 upgrade cleanup 2026-06-04 18:38:25 -04:00
Ryan Hughes 0804962619 Improve update and migration flow 2026-06-04 18:38:25 -04:00
Ryan Hughes babfafa5e9 Split user defaults into skel seed, finalize, and resync
Reorganizes Omarchy 4 around three layers for populating $HOME:

  Seed:     omarchy-settings ships defaults to /etc/skel; useradd -m
            copies them on user creation
  Finalize: omarchy-finalize-user (renamed from omarchy-setup-user)
            handles only the runtime tweaks /etc/skel can't do — skill
            symlinks, xdg-user-dirs, default browser/mailto, vconsole→hypr
            keyboard sync, and install/user/all.sh
  Resync:   omarchy-reinstall-configs is the explicit, destructive
            resync of /etc/skel into an existing user's $HOME

Package-owned files move out of config/ into default/, where the
omarchy-settings PKGBUILD installs them to real system paths:

  config/environment.d/fcitx.conf            -> /usr/lib/environment.d/
  config/fontconfig/fonts.conf               -> /usr/share/fontconfig/conf.avail/
  config/mimeapps.list                       -> /usr/share/applications/
  config/omarchy.ttf                         -> /usr/share/fonts/omarchy/
  config/systemd/user/*.service              -> /usr/lib/systemd/user/
  config/uwsm/default                        -> /usr/share/omarchy/default/uwsm/
  config/uwsm/env                            -> /usr/share/uwsm/env.d/10-omarchy
  config/xdg-terminals.list                  -> /usr/share/xdg-terminal-exec/

omarchy-upgrade-to-4 grows a 'retire' action (renamed from 'move' to
clarify nothing is copied — the system path is owned by the new package
once the user's hash-matched ~/.config copy is removed). Mismatched
copies are kept as backups so user overrides survive the upgrade.

Other simplifications:

  - Single env bootstrap at default/bash/env-bootstrap sourced by
    /etc/profile.d/omarchy.sh, /etc/skel/.bashrc,
    /usr/share/uwsm/env.d/10-omarchy, and default/bash/envs. PATH
    prepend only in dev-link mode (production uses /usr/bin/omarchy-*).
  - omarchy-refresh-config reads from /etc/skel/.config so refresh
    means 'snap to skel'.
  - omarchy-reinstall-configs collapses to 'cp -af /etc/skel/. ~/'
    plus limine/plymouth/nvim refresh.
  - omarchy-font-set uses awk against our own 30-omarchy.conf instead
    of xmlstarlet; xmlstarlet dropped from omarchy-base.packages.
  - Defer user systemd enables (bt-agent, sleep-lock,
    recover-internal-monitor) to first-run via
    install/user/first-run/enable-user-units.sh; delete
    omarchy-user-systemctl-enable and the per-hardware install
    scripts that called it.
  - Wireplumber bluetooth-a2dp-autoconnect.conf moves to config/ so
    /etc/skel ships it; install/user/hardware/bluetooth.sh deleted.
  - Default terminal switched to foot.desktop.
  - docs/file-layout.md documents the three-layer model and the
    build-time repo→path map.
2026-06-04 18:38:25 -04:00
Ryan Hughes bcf07e424a Add Omarchy 4 upgrade bridge
Introduce a self-contained omarchy-upgrade-to-4 command for legacy installs and remove the 4.0 package-layout transition from the normal migration runner.
2026-06-04 18:37:32 -04:00
Ryan Hughes 75cb4f7195 Make setup ISO-only
Remove legacy online installer entrypoints, collapse migrations for 4.0, and move setup responsibilities into target-side system, hardware, and user commands.
2026-06-04 18:37:32 -04:00
David Heinemeier Hansson 5812a7d04a No leading zeros on the date in the bar 2026-06-01 11:59:30 +02:00
Ryan Hughes f8e82dac6b Improve omarchy-config-shell-bar 2026-05-27 16:44:43 -04:00
David Heinemeier Hansson 3986522d6e Extend shell bar config command 2026-05-27 11:20:42 +02:00
David Heinemeier Hansson e91aa9647b Add shell bar config commands 2026-05-27 10:41:12 +02:00
David Heinemeier Hansson f520f9ff22 Simplify 2026-05-27 10:28:49 +02:00
David Heinemeier Hansson 67dd7a2953 Add Tailscale panel connection controls 2026-05-26 22:07:20 +02:00
David Heinemeier Hansson 8ffb498168 Move shell tests under shell runner 2026-05-25 15:16:53 +02:00