Author SHA1 Message Date
ZacharyZhang-NYandClaude Fable 5 aa87ed97c1 Dim prompts use the documented dim exec form
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 15:03:35 -04:00
ZacharyZhang-NYandClaude Fable 5 a3cd27dba3 Add Dim Agent CLI to the agent roster and Install > AI
npm dimcode (bin: dim) rides the mise path like Deep Code; entries
land alphabetically in the Default Agent and Install > AI menus.
CLI-only per dimagent.cn, login handled by the CLI itself.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 14:59:18 -04:00
ZacharyZhang-NYandClaude Fable 5 0812b28527 Detect the missing official repo dbs by core.db, not any db
pacstrap leaves the offline repo's db in the target's sync dir, so
the any-db glob skipped the first-run update path on real installs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 13:50:11 -04:00
ZacharyZhang-NYandClaude Fable 5 9bf870449b Link kimi into ~/.local/bin after the official install
The installer only appends PATH to .bashrc, so the fresh binary was
invisible to the launching process and the login never started.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 13:46:23 -04:00
ZacharyZhang-NYandClaude Fable 5 9be4d24d6d App installs route first-time updates through omarchy-update
Direct pacman -Syu trips the update pacman guard once real upgrades
exist; the catalog installer now runs the upstream update pipeline
interactively and fails fast with instructions when non-interactive.
omarchy-cn-kimi-install gains its executable bit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 13:27:38 -04:00
ZacharyZhang-NYandClaude Fable 5 a317bfb36f Fix China app installs and wire the CN AI stack into Install > AI
Offline installs ship no pacman sync db, so yay resolved official
repo dependencies as AUR packages and every catalog install failed;
omarchy-cn-app-install now runs a full -Syu first (verified end to
end: WeChat builds and installs in the VM). Kimi Code gains a real
installer command reused by the harness registry, the default-agent
path, and new Install > AI entries (Kimi Code, Deep Code, AI Hub),
and the AI Hub wizard offers to launch native harnesses immediately
so their official CLI login runs, matching how Claude Code works.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 13:17:52 -04:00
ZacharyZhang-NYandClaude Fable 5 0f888ffe31 Bump cn/release to 5 after publishing 4.0.0.alpha-cn.4
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 12:25:27 -04:00
ZacharyZhang-NY 41ddd9052a Update README.md 2026-08-25 16:14:20 +00:00
ZacharyZhang-NYandClaude Fable 5 68a994b223 Document the Chinese-first defaults in the OmarchyCN layer
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 12:03:04 -04:00
ZacharyZhang-NYandClaude Fable 5 d7938dd10b Localize the fingerprint first-run notice
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 11:21:31 -04:00
ZacharyZhang-NYandClaude Fable 5 f338f6cb15 Localize update confirmation and first-run notifications
The update prompt speaks Chinese and points What's new at the
OmarchyCN releases page; the Wi-Fi, update, keybindings, dictation,
and default-agent first-run notices follow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 11:18:02 -04:00
ZacharyZhang-NYandClaude Fable 5 b85a998404 Keybindings menu test fixtures follow localization
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 11:16:37 -04:00
ZacharyZhang-NYandClaude Fable 5 e32f1eca8e Binding-conflicts test follows localized descriptions
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 11:12:43 -04:00
ZacharyZhang-NYandClaude Fable 5 6e3eb678b1 Bar panel test expectation follows localization
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 11:10:09 -04:00
ZacharyZhang-NYandClaude Fable 5 2fce4924e6 Remaining scratchpad test expectations follow localization
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 11:09:55 -04:00
ZacharyZhang-NYandClaude Fable 5 6e73f9f797 Scratchpad binding test follows the localized description
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 11:09:39 -04:00
ZacharyZhang-NYandClaude Fable 5 9e1bb767c0 Voxtype binding test follows localized descriptions
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 11:09:23 -04:00
ZacharyZhang-NYandClaude Fable 5 2eff2a37b3 Localize dynamic bind templates and the Super+K matcher
Workspace, group-window, and bar-panel loops emit Chinese
descriptions; omarchy-menu-keybindings' merge list and priority
patterns follow them, so combined chords and curated ordering
survive localization. Affected tests updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 11:09:05 -04:00
ZacharyZhang-NYandClaude Fable 5 e673e63baa Localize keybinding descriptions for the Super+K cheatsheet
All 176 bind descriptions across the shipped Hyprland binding files
switch to Simplified Chinese; brand names stay English.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 11:03:38 -04:00
ZacharyZhang-NYandClaude Fable 5 907f7b81aa Fix kimi install error masking, prompt flag, and setup default hop
The official installer downloads to a temp file so curl failures
cannot be masked by the pipe; Kimi prompts use --prompt per the CLI
reference; the AI Hub wizard now offers omarchy-cn-ai-default for
claude-code/codex profiles so the combo really becomes the default
agent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 11:01:50 -04:00
ZacharyZhang-NYandClaude Fable 5 8ce169f7fa Keep the agent menu alphabetical with the CN additions
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 10:56:16 -04:00
ZacharyZhang-NYandClaude Fable 5 0778554984 Add Kimi Code and Deep Code to the default agent roster
kimi installs through its official script rather than mise;
deepcode rides the existing mise path as npm:@vegamo/deepcode-cli.
Both gain launch branches and Default Agent menu entries, plus an
AI Hub entry for provider combos (Kimi/DeepSeek/Z.AI/MiniMax over
Claude Code) via the omarchycn setup wizard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 10:55:45 -04:00
ZacharyZhang-NYandClaude Fable 5 6b491d52be Add MiniMax as an Anthropic-compatible AI provider
MiniMax-M3[1m] via api.minimaxi.com/anthropic per the official
Claude Code token-plan docs; the claude-code combo enters the
compatibility matrix as stable with mock regression coverage,
including the Haiku-slot fallback to the single model.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 10:49:53 -04:00
ZacharyZhang-NYandClaude Fable 5 e57d2a3424 Plugin menu test expects the localized labels
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 10:38:21 -04:00
ZacharyZhang-NYandClaude Fable 5 b7dd8c7125 Menu search test follows the localized labels
The exact-label ranking case now queries in Chinese, and a new case
pins English reachability through the leaf id.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 10:37:59 -04:00
ZacharyZhang-NYandClaude Fable 5 f182234796 Localize the Omarchy menu and add AI Hub and China apps sections
All category and action labels switch to Simplified Chinese with
brand names kept in English per the PRD terminology rule. The
OmarchyCN section gains an AI Hub submenu (wizard, profiles, test,
doctor) and a China apps submenu backed by the cn/apps.json catalog.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 10:36:42 -04:00
ZacharyZhang-NYandClaude Fable 5 b7aa5d9d7d Drop mailto handler for removed HEY launcher from mimeapps
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 10:33:49 -04:00
ZacharyZhang-NYandClaude Fable 5 43f274ce63 Docs: stop claiming removed apps ship by default
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 10:32:51 -04:00
ZacharyZhang-NYandClaude Fable 5 763ebb83c3 Purge remaining references to removed western apps
The quattro upgrade script no longer registers the HEY mailto
handler, installer tips stop naming LibreOffice, and the hotkeys,
GUIs, and web-apps manuals drop the entries for removed apps.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 10:30:28 -04:00
ZacharyZhang-NYandClaude Fable 5 6d8acfca9f Drop western default apps from the CN default set
Basecamp, Discord, HEY, Google Contacts/Maps/Messages/Photos,
WhatsApp, and X lose their shipped launchers, webapp keybindings,
and the whatsapp-slim extension; aether, libreoffice-fresh, and
obs-studio leave the base package set. The HEY mailto handler and
its registration go with them. Preinstall add/remove lists, the
launcher hide list, and chromium flags stay in sync.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 10:23:52 -04:00
ZacharyZhang-NYandClaude Fable 5 7150f63544 Ship Chinese IME and font fallback by default on zh_CN systems
fcitx5-rime, fcitx5-chinese-addons, and fcitx5-configtool join the
base package set, and a provisioning step seeds the Rime profile and
CJK fontconfig for users on systems whose locale is zh_CN, so Chinese
input works at first boot without omarchycn setup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 10:14:00 -04:00
ZacharyZhang-NYandClaude Fable 5 e71a9de012 Install zh_CN.UTF-8 as system language for Chinese installs
The configurator wrote en_US.UTF-8 unconditionally, so a Chinese
install booted into an English system. sys_lang now follows the
installer language, and both zh_CN and en_US locales are always
generated in the target.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 10:00:14 -04:00
ZacharyZhang-NYandClaude Fable 5 960e0a6211 Bump cn/release to 4 after publishing 4.0.0.alpha-cn.3
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 08:57:39 -04:00
ZacharyZhang-NYandClaude Fable 5 e0b21ff8fb Fix shell-quoting leak in setup-form English strings
Three literals carried ''' escape sequences verbatim, so sourcing
the English branch executed stray words and aborted the first-boot
owner form under set -e.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 08:37:33 -04:00
ZacharyZhang-NYandClaude Fable 5 ba9a265e4a Fix GUI fallback misfire and untranslated failure-menu case patterns
The cage wrapper treated the installer session exit code as cage
availability, so an installer abort re-ran the English VT flow. A
gui-started marker now separates the two. Dashboard failure-menu
cases for shell/poweroff now match the localized choice strings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 02:13:27 -04:00
ZacharyZhang-NYandClaude Fable 5 1245b749af Logo word gap; translate welcome, cfdisk, and partitioning progress strings
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 01:43:21 -04:00
ZacharyZhang-NYandClaude Fable 5 84514840f5 Chinese installer onboarding: bilingual setup form, vendored ISO patch
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 01:33:35 -04:00
ZacharyZhang-NYandClaude Fable 5 48218b2d5f Brand boot splash and plymouth wordmark as OMARCHY CN
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 01:26:05 -04:00
ZacharyZhang-NYandClaude Fable 5 6ce3bbf8d6 Merge upstream sync b86d4505 (README resolved toward CN rewrite)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-25 01:18:23 -04:00
ZacharyZhang-NYandClaude Fable 5 0bb9a96612 Begin cn.3 development cycle
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-24 23:25:41 -04:00
ZacharyZhang-NYandClaude Fable 5 9546fe98b9 Update: refuse pre-channel downgrades; doc docker restore scope
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-24 23:24:39 -04:00
ZacharyZhang-NYandClaude Fable 5 2f5f02d3e5 Update: stable-channel empty tag list fails with message, not silently
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-24 23:21:11 -04:00
ZacharyZhang-NYandClaude Fable 5 eb149f30c4 M5 review fixes: key download step, honest update paths, ordered checklist, channel semantics
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-24 23:19:26 -04:00
ZacharyZhang-NYandClaude Fable 5 9145679261 Document the OmarchyCN layer in AGENTS.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-24 22:17:32 -04:00
ZacharyZhang-NYandClaude Fable 5 786544edf3 Add release checklist, sync SLA, security response, channel command
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-24 22:17:06 -04:00
ZacharyZhang-NYandClaude Fable 5 708651fa8e Add Chinese user manual (install, mirrors, IME, AI, recovery)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-24 22:16:34 -04:00
ZacharyZhang-NYandClaude Fable 5 79d3a65441 Remove temp diagnostics workflow
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-24 22:15:24 -04:00
ZacharyZhang-NYandClaude Fable 5 17e0beb7a6 Temp host diagnostics workflow
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
2026-08-24 22:09:10 -04:00
Sameer PokharelandGitHub b86d4505c1 Renumber the README manual TOC after Extra themes was dropped (#8089)
Dropping manual/43-extra-themes.md shifted every chapter after it down
by one. The TOC rode along on the old numbers, so its last ten links
404.
2026-08-24 21:59:17 +02:00
06a3dbca42 Offer to reboot when toggling sudoless Docker; show only the relevant menu entry (#8098)
* Offer to reboot when toggling sudoless Docker; show only the relevant menu entry

Group membership only takes effect on a fresh session, and in practice a logout
or newgrp isn't enough — only a reboot reliably applies it. So the setup/remove
commands now flag the reboot and offer to do it now with a gum confirm (like the
GPU toggle), and the notices say "after a reboot" instead of pointing at logout
or newgrp. The existing-user migration passes OMARCHY_DEFER_REBOOT so it does not
prompt mid-update — omarchy-update-restart still handles the reboot once the whole
update finishes.

The Setup > Security menu also showed Sudoless Docker under both Setup and
Remove. Condition the Setup entry on the group being absent (Remove already
conditions on it being present), so only the applicable one appears.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gb7x6poap4hGCndPx5qt5T

* Ask omarchy-sudo-docker whether Docker needs sudo

Every place that chooses between talking to Docker directly and elevating was
testing group membership by hand, and the menu guards tested the wrong thing:
they read the running session's groups, which do not change until the reboot,
so after enabling sudoless Docker the menu still offered Setup — the one action
that could no longer do anything — while Remove stayed hidden.

Add omarchy-sudo-docker as the single answer, with the two questions that
actually differ in that window. By default it asks whether this session can
reach the socket, which is what decides if a command must elevate: lazydocker
and the Windows VM keep prompting until the reboot lands. With --configured it
asks whether the account is set up for sudoless Docker, which is what the menu
and the toggles need, so the menu switches to the action that can change state
as soon as the group is written.

Also correct a comment: nothing surfaces reboot-required in the bar; it is
omarchy-update-restart that reads it during a later update.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gb7x6poap4hGCndPx5qt5T

---------

Co-authored-by: David Heinemeier Hansson <david@hey.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 21:58:27 +02:00
96 changed files with 2220 additions and 901 deletions
+17 -1
View File
@@ -11,13 +11,29 @@ matching guide before starting:
- [`agents/skills/visual-verification.md`](agents/skills/visual-verification.md) - verifying any change with a visual effect in the running UI
- [`agents/skills/migrations.md`](agents/skills/migrations.md) - creating or changing migrations under `migrations/`
# OmarchyCN Layer
This fork carries the OmarchyCN China-integration layer on top of upstream `basecamp/omarchy`:
- `bin/omarchycn` routes to `omarchy cn <command>`; all cn commands are `bin/omarchy-cn-*` and follow the upstream bin conventions (metadata, helpers, `$OMARCHY_PATH` — sole exception: the overlay installer bootstraps by resolving its own checkout)
- `cn/` holds the data layer: `mirrors.json`, `dev-mirrors.json`, `apps.json`, `registry/` (AI providers/harnesses/compatibility), `fcitx5/`, `fontconfig/`, `keys/`, `lib/` (sourced helpers), `release` (cn release number)
- cn migrations live in `cn/migrations/*.sh`, run by `omarchy-cn-update` with per-file completion markers under `~/.local/state/omarchycn/`
- Packaging: `packages/omarchy-pkgs-cn.patch` must be applied to the sibling `omarchy-pkgs` checkout so `omarchy-dev` ships `cn/`; keyring in `packages/omarchycn-keyring/`
- ISO: `packages/omarchy-iso-cn.patch` must be applied to the sibling `omarchy-iso` checkout — Chinese installer (cage+foot graphical console with English VT fallback) and live-env packages
- Release process: `docs/release-checklist.md`; signing: `docs/release-signing.md`; pacman repo: `docs/pacman-repo.md`
- Chinese-first defaults: OmarchyCN ships Simplified Chinese as the default UX. User-visible strings in `default/omarchy/omarchy-menu.jsonc` (all labels), `default/hypr/bindings/*.lua` (bind descriptions, including the generated workspace/group/panel loops), `bin/omarchy-menu-keybindings` (its merge list and priority patterns must match the shipped Chinese descriptions), `bin/omarchy-update-confirm`, and `install/user/first-run/*` notifications are Chinese with brand names kept in English; menu search stays reachable in English through leaf ids. When syncing upstream, translate new strings in these files and resolve string conflicts toward our Chinese text.
- Default set changes vs upstream: `install/omarchy-base.packages` adds fcitx5-rime/chinese-addons/configtool and drops aether/libreoffice-fresh/obs-studio; the Basecamp/Discord/HEY/Google/WhatsApp/X launchers, their webapp keybindings, the whatsapp-slim extension, and the HEY mailto handler are removed (preinstall add/remove lists, launcher.hides, chromium flags, and mimeapps stay in sync); `install/user/cn-chinese.sh` seeds IME/font config on zh_CN systems
- AI: `cn/registry/` drives providers (DeepSeek/Kimi/Z.AI/MiniMax) with zero-code adapters; `omarchy-default-agent` also accepts kimi (official installer script, not mise) and deepcode (mise npm); the Default Agent menu carries them plus an AI Hub combo entry
- Upstream sync: `.gitea/workflows/upstream-sync.yml` opens a PR per upstream change; keep upstream file edits minimal and inventoried (currently: one `GROUP_DESCRIPTIONS[cn]` line in `bin/omarchy`, the localized menu/bindings/update/first-run surfaces above, the western-app removals above, a rewritten `README.md` (known recurring sync conflict, resolve toward ours), `AGENTS.md` additions, and two `.gitignore` lines)
- cn tests: `test/shell.d/omarchycn-test.sh`, `test/shell.d/omarchycn-ai-test.sh`; localized UI expectations live in the upstream suites (menu, keybindings-menu, hyprland-default-config, binding-conflicts, clock, screenrecording, update-disk-space)
# Documentation Layout
Three documentation trees, split by genre and audience:
- `agents/skills/` - task procedure ("do this when doing X"), for anyone working on the codebase
- `docs/` - reference on how the system is shaped (file layout, update pipeline, theming, shell architecture), for anyone working on the codebase; skills link here for depth
- `manual/` - end-user documentation for using Omarchy, published; never codebase internals
- `manual/` - end-user documentation for using Omarchy, published; never codebase internals. Chinese user manual for the cn layer lives in `manual/zh-cn/`
# Style
+1 -3
View File
@@ -2,7 +2,7 @@
面向中国开发者的 [Omarchy](https://omarchy.org) 下游发行版:完整保留 Omarchy 的 Arch + Hyprland 桌面体验,为中国网络环境、中文使用习惯与国内 AI 服务做系统级增强。
当前版本 `4.0.0.alpha-cn.1`(基于上游 `quattro` 分支构建的基线 ISO)。
当前版本 `4.0.0.alpha-cn.4`(基于上游 `quattro` 分支构建的基线 ISO)。
## 特性
@@ -15,8 +15,6 @@
- Btrfs + Snapper 更新前快照与系统回滚
- archiso 离线安装镜像,支持无人值守安装与双系统引导
OmarchyCN 增强(开发中,按路线图逐步交付):
- **中国镜像管理**Arch / npm / pip / Cargo / Go 等软件源测速、自动选择与故障切换
- **中文环境开箱即用**zh_CN locale、思源黑体 / 宋体字体栈、高分屏分数缩放预设
- **中文输入法**Fcitx5 + Rime 预配置,Wayland / GTK / Qt / Electron 全栈兼容,快捷键冲突自动处理
-8
View File
@@ -1,8 +0,0 @@
[Desktop Entry]
Version=1.0
Name=Basecamp
Exec=omarchy-launch-webapp https://launchpad.37signals.com
Terminal=false
Type=Application
Icon=basecamp
StartupNotify=true
-8
View File
@@ -1,8 +0,0 @@
[Desktop Entry]
Version=1.0
Name=Discord
Exec=omarchy-launch-webapp https://discord.com/channels/@me
Terminal=false
Type=Application
Icon=omarchy-discord
StartupNotify=true
-8
View File
@@ -1,8 +0,0 @@
[Desktop Entry]
Version=1.0
Name=Google Contacts
Exec=omarchy-launch-webapp https://contacts.google.com/
Terminal=false
Type=Application
Icon=google-contacts
StartupNotify=true
-8
View File
@@ -1,8 +0,0 @@
[Desktop Entry]
Version=1.0
Name=Google Maps
Exec=omarchy-launch-webapp https://maps.google.com
Terminal=false
Type=Application
Icon=google-maps
StartupNotify=true
-8
View File
@@ -1,8 +0,0 @@
[Desktop Entry]
Version=1.0
Name=Google Messages
Exec=omarchy-launch-webapp https://messages.google.com/web/conversations
Terminal=false
Type=Application
Icon=google-messages
StartupNotify=true
-8
View File
@@ -1,8 +0,0 @@
[Desktop Entry]
Version=1.0
Name=Google Photos
Exec=omarchy-launch-webapp https://photos.google.com/
Terminal=false
Type=Application
Icon=google-photos
StartupNotify=true
-9
View File
@@ -1,9 +0,0 @@
[Desktop Entry]
Version=1.0
Name=HEY
Exec=omarchy-webapp-handler-hey %u
Terminal=false
Type=Application
Icon=hey
StartupNotify=true
MimeType=x-scheme-handler/mailto
-8
View File
@@ -1,8 +0,0 @@
[Desktop Entry]
Version=1.0
Name=WhatsApp
Exec=omarchy-launch-webapp https://web.whatsapp.com/
Terminal=false
Type=Application
Icon=whatsapp
StartupNotify=true
-8
View File
@@ -1,8 +0,0 @@
[Desktop Entry]
Version=1.0
Name=X
Exec=omarchy-launch-webapp https://x.com/
Terminal=false
Type=Application
Icon=x
StartupNotify=true
Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

+16
View File
@@ -99,6 +99,22 @@ pi)
command=(pi)
[[ -n ${prompt:-} ]] && command+=("$prompt")
;;
kimi)
command=(kimi)
[[ -n ${prompt:-} ]] && command+=(--prompt "$prompt")
;;
deepcode)
command=(deepcode)
[[ -n ${prompt:-} ]] && command+=("$prompt")
;;
dim)
# `dim exec` is the documented one-shot prompt form
if [[ -n ${prompt:-} ]]; then
command=(dim exec "$prompt")
else
command=(dim)
fi
;;
*)
echo "Unsupported default agent: $agent" >&2
exit 1
+10 -1
View File
@@ -40,7 +40,10 @@ proto=$(cn_ai_harness_field "$harness" protocol)
if [[ $proto == "native" ]]; then
omarchy-cn-ai-profile-create "$name" "$harness" "$provider" "$model"
omarchy-cn-ai-profile-use "$name"
echo "$harness 自管鉴权:首次启动时按其官方流程登录/配置"
echo "$harness 自管鉴权:首次启动时在 CLI 内完成官方登录"
if gum confirm "立即启动并完成官方登录?"; then
exec omarchy-cn-ai-launch "$name"
fi
echo "完成。启动: omarchycn ai launch"
exit 0
fi
@@ -73,4 +76,10 @@ fi
rm -f "$profile_file.omarchycn-prev"
omarchy-cn-ai-profile-use "$name"
if [[ $harness == "claude-code" || $harness == "codex" ]]; then
if gum confirm "设为系统默认 AgentSuper+Shift+Ctrl+A)?"; then
omarchy-cn-ai-default "$name"
fi
fi
echo "完成。启动: omarchycn ai launch"
+11
View File
@@ -32,6 +32,17 @@ fi
case "$source_type" in
aur)
# Offline installs leave only offline.db in sync; without the official
# repo dbs yay treats repo deps as AUR. Upgrades must go through the
# omarchy-update pipeline (pacman guard).
if [[ ! -f /var/lib/pacman/sync/core.db ]]; then
if [[ ! -t 0 ]]; then
echo "首次安装应用前请先完成一次系统更新: omarchy-update" >&2
exit 1
fi
echo "首次安装应用前需要完成一次系统更新。"
omarchy-update
fi
omarchy-pkg-aur-add "$(jq -r '.package' <<<"$entry")"
;;
webapp)
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
# omarchy:summary=Show or set the OmarchyCN release channel
# omarchy:args=[stable|beta|nightly]
# omarchy:examples=omarchycn channel | omarchycn channel beta
set -euo pipefail
CHANNEL_FILE="$HOME/.config/omarchycn/channel"
if (( $# == 0 )); then
if [[ -f $CHANNEL_FILE ]]; then
cat "$CHANNEL_FILE"
else
echo "beta (default)"
fi
exit 0
fi
case "$1" in
stable | beta | nightly)
mkdir -p "${CHANNEL_FILE%/*}"
echo "$1" > "$CHANNEL_FILE"
echo "Channel: $1"
;;
*)
echo "Unknown channel: $1 (stable|beta|nightly)" >&2
exit 1
;;
esac
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
# omarchy:summary=Install Kimi Code with the official installer script
# omarchy:examples=omarchycn kimi install
set -euo pipefail
if omarchy-cmd-present kimi; then
echo "Kimi Code 已安装"
exit 0
fi
installer=$(mktemp)
trap 'rm -f "$installer"' EXIT
curl -fsSL https://code.kimi.com/kimi-code/install.sh -o "$installer"
bash "$installer"
# The official installer only touches .bashrc PATH; expose kimi now
if [[ ! -x $HOME/.kimi-code/bin/kimi ]]; then
echo "kimi binary missing after the official installer" >&2
exit 1
fi
mkdir -p "$HOME/.local/bin"
ln -sf "$HOME/.kimi-code/bin/kimi" "$HOME/.local/bin/kimi"
+41 -2
View File
@@ -19,10 +19,49 @@ if [[ ! -d $src/.git ]]; then
exit 1
fi
channel=$(omarchy-cn-channel)
channel=${channel%% *}
old=$(git -C "$src" rev-parse --short HEAD)
git -C "$src" pull --ff-only
git -C "$src" fetch -q --tags origin
case "$channel" in
nightly)
git -C "$src" checkout -q quattro
git -C "$src" pull --ff-only origin quattro
;;
beta)
tag=$(git -C "$src" tag -l "*-cn.*" | sort -V | tail -1)
if [[ -z $tag ]]; then
echo "beta 通道无可用发布 tag" >&2
exit 1
fi
git -C "$src" checkout -q "$tag"
;;
stable)
tag=$(git -C "$src" tag -l "*-cn.*" | grep -vE "alpha|beta|rc" | sort -V | tail -1 || true)
if [[ -z $tag ]]; then
echo "当前尚无 stable 发布(omarchycn channel beta 可跟随预发布)" >&2
exit 1
fi
git -C "$src" checkout -q "$tag"
;;
*)
echo "Unknown channel: $channel" >&2
exit 1
;;
esac
# Never move onto a tree that predates the channel mechanism: its update
# command cannot switch back, stranding the install
if [[ ! -f $src/bin/omarchy-cn-channel ]]; then
git -C "$src" checkout -q "$old"
echo "$channel 通道的目标发布早于通道机制,已回退;请使用 nightly 或更新的发布" >&2
exit 1
fi
new=$(git -C "$src" rev-parse --short HEAD)
echo "Source: $old -> $new"
echo "Channel: $channel, source: $old -> $new"
"$src/bin/omarchy-cn-install-overlay"
+19 -3
View File
@@ -1,8 +1,8 @@
#!/bin/bash
# omarchy:summary=Set and launch the default coding agent
# omarchy:args=[pi|omp|opencode|ori|claude|codex|grok|agy|copilot|crush]
# omarchy:examples=omarchy default agent | omarchy default agent codex | omarchy default agent claude
# omarchy:args=[pi|omp|opencode|ori|claude|codex|grok|agy|copilot|crush|kimi|deepcode|dim]
# omarchy:examples=omarchy default agent | omarchy default agent codex | omarchy default agent kimi
installing=false
if [[ ${1:-} == "--install" ]]; then
@@ -34,14 +34,29 @@ crush) agent="crush"; name="Crush" ;;
grok) agent="grok"; name="Grok"; agent_package="npm:@xai-official/grok" ;;
agy | antigravity | antigravity-cli | gemini | gemini-cli) agent="agy"; name="Antigravity"; agent_package="antigravity-cli" ;;
copilot | github-copilot) agent="copilot"; name="GitHub Copilot" ;;
kimi | kimi-code) agent="kimi"; name="Kimi Code" ;;
deepcode | deep-code) agent="deepcode"; name="Deep Code"; agent_package="npm:@vegamo/deepcode-cli" ;;
dim | dimcode | dim-agent) agent="dim"; name="Dim Agent"; agent_package="npm:dimcode" ;;
*)
echo "Usage: omarchy-default-agent <pi|omp|opencode|ori|claude|codex|grok|agy|copilot|crush>"
echo "Usage: omarchy-default-agent <pi|omp|opencode|ori|claude|codex|grok|agy|copilot|crush|kimi|deepcode|dim>"
exit 1
;;
esac
agent_package=${agent_package:-$agent}
# Kimi Code installs via its official script, not mise
if [[ $agent == "kimi" ]]; then
if omarchy-cmd-missing kimi; then
if [[ $installing == "false" ]]; then
exec omarchy-launch-floating-terminal-with-presentation omarchy-default-agent --install kimi
fi
if ! omarchy-cn-kimi-install; then
echo "Could not install $name with the official installer" >&2
exit 1
fi
fi
else
if [[ $installing == "false" ]] && ! mise where "$agent_package" &>/dev/null; then
exec omarchy-launch-floating-terminal-with-presentation omarchy-default-agent --install "$agent"
fi
@@ -54,6 +69,7 @@ if ! mise use -g "$agent_package"; then
fi
exit 1
fi
fi
mkdir -p "$(dirname "$agent_file")"
printf '%s\n' "$agent" >"$agent_file"
-3
View File
@@ -12,13 +12,10 @@ if gum confirm "Are you sure you want to restore all preinstalled web apps, TUI
# Mirrors the list in omarchy-remove-preinstalls; both track omarchy-base.packages
if ! omarchy-pkg-add \
aether \
cliamp \
libreoffice-fresh \
xournalpp \
pinta \
obsidian \
obs-studio \
kdenlive \
moonlight-qt \
lazydocker \
+7 -5
View File
@@ -9,10 +9,12 @@
# no prompt. lazydocker needs the root-owned Docker socket, so when the group is
# absent, gate that access behind a polkit prompt. If the user has opted into
# sudoless Docker (omarchy-setup-security-sudoless-docker), the socket is already
# reachable, so run lazydocker directly. pkexec sanitizes the environment, so
# carry TERM through for the TUI to render and run lazydocker from root's PATH.
if id -nG 2>/dev/null | grep -qw docker; then
exec lazydocker
else
# reachable, so run lazydocker directly — omarchy-sudo-docker answers that for
# this session, so the prompt stays until the reboot that grants the group.
# pkexec sanitizes the environment, so carry TERM through for the TUI to render
# and run lazydocker from root's PATH.
if omarchy-sudo-docker; then
exec pkexec /usr/bin/env TERM="${TERM:-xterm-256color}" lazydocker
else
exec lazydocker
fi
+47 -47
View File
@@ -338,10 +338,10 @@ static_bindings() {
# for interchangeably belong on one row.
alternative_chord_actions() {
cat <<'ACTIONS'
Close window
Calculator
Toggle scratchpad
Move window to scratchpad
关闭窗口
计算器
切换便签区
窗口移入便签区
ACTIONS
}
@@ -442,58 +442,58 @@ prioritize_entries() {
sub(/ \/ [^→]*→/, " →", line)
prio = 50
if (match(line, /Keybindings/)) prio = 0
if (match(line, /Omarchy menu/)) prio = 1
if (match(line, /Terminal/)) prio = 2
if (match(line, /Browser/) && !match(line, /Browser[[:space:]]*\(/) && !match(line, /SUPER SHIFT.*\+.*B.*→.*Browser/)) prio = 3
if (match(line, /File manager/) && !match(line, /File manager \(cwd\)/)) prio = 4
if (match(line, /快捷键/)) prio = 0
if (match(line, /Omarchy 菜单/)) prio = 1
if (match(line, /终端/)) prio = 2
if (match(line, /浏览器/) && !match(line, /浏览器(/) && !match(line, /SUPER SHIFT.*\+.*B.*→.*浏览器/)) prio = 3
if (match(line, /文件管理器/) && !match(line, /文件管理器(当前目录)/)) prio = 4
if (match(line, /Launch apps/)) prio = 5
if (match(line, /System menu/)) prio = 6
if (match(line, /Theme menu/)) prio = 7
if (match(line, /Full screen/)) prio = 8
if (match(line, /Full width/)) prio = 9
if (match(line, /Close window/)) prio = 10
if (match(line, /Close all windows/)) prio = 11
if (match(line, /Lock system/)) prio = 12
if (match(line, /Toggle window floating/)) prio = 13
if (match(line, /Toggle window split/)) prio = 14
if (match(line, /Pop window/)) prio = 15
if (match(line, /Universal/)) prio = 16
if (match(line, /Clipboard/)) prio = 17
if (match(line, /系统菜单/)) prio = 6
if (match(line, /主题菜单/)) prio = 7
if (match(line, /全屏/)) prio = 8
if (match(line, /全宽/)) prio = 9
if (match(line, /关闭窗口/)) prio = 10
if (match(line, /关闭所有窗口/)) prio = 11
if (match(line, /锁定系统/)) prio = 12
if (match(line, /切换窗口浮动/)) prio = 13
if (match(line, /切换窗口分割/)) prio = 14
if (match(line, /弹出窗口/)) prio = 15
if (match(line, /通用/)) prio = 16
if (match(line, /剪贴板/)) prio = 17
if (match(line, /Audio controls/)) prio = 18
if (match(line, /Bluetooth controls/)) prio = 19
if (match(line, /Wifi controls/)) prio = 20
if (match(line, /Emojis/)) prio = 21
if (match(line, /Color picker/)) prio = 22
if (match(line, /Screenshot/)) prio = 23
if (match(line, /Screenrecording/)) prio = 24
if (match(line, /表情/)) prio = 21
if (match(line, /取色器/)) prio = 22
if (match(line, /截图/)) prio = 23
if (match(line, /录屏/)) prio = 24
if (match(line, /Tmux/)) prio = 25
if (match(line, /Herdr/)) prio = 26
if (match(line, /SUPER SHIFT.*\+.*B.*→.*Browser/)) prio = 27
if (match(line, /File manager \(cwd\)/)) prio = 28
if (match(line, /(Switch|Next|Former|Previous).*workspace/)) prio = 29
if (match(line, /Move window to workspace/)) prio = 30
if (match(line, /Move window silently to workspace/)) prio = 31
if (match(line, /Swap window/)) prio = 32
if (match(line, /Focus/)) prio = 33
if (match(line, /Move window$/)) prio = 34
if (match(line, /Resize window/)) prio = 35
if (match(line, /Expand window/)) prio = 36
if (match(line, /Shrink window/)) prio = 37
if (match(line, /scratchpad/)) prio = 38
if (match(line, /notification/)) prio = 39
if (match(line, /Toggle window transparency/)) prio = 40
if (match(line, /Toggle workspace gaps/)) prio = 41
if (match(line, /Toggle nightlight/)) prio = 42
if (match(line, /Toggle locking/)) prio = 43
if (match(line, /group/)) prio = 94
if (match(line, /Scroll active workspace/)) prio = 95
if (match(line, /SUPER SHIFT.*\+.*B.*→.*浏览器/)) prio = 27
if (match(line, /文件管理器(当前目录)/)) prio = 28
if (match(line, /(切换到|下一|上一|上次)工作区/)) prio = 29
if (match(line, /窗口移到工作区/)) prio = 30
if (match(line, /窗口静默移到工作区/)) prio = 31
if (match(line, /互换/)) prio = 32
if (match(line, /聚焦/)) prio = 33
if (match(line, /移动窗口$/)) prio = 34
if (match(line, /调整窗口大小/)) prio = 35
if (match(line, /扩展窗口/)) prio = 36
if (match(line, /收缩窗口/)) prio = 37
if (match(line, /便签区/)) prio = 38
if (match(line, /通知/)) prio = 39
if (match(line, /切换窗口透明/)) prio = 40
if (match(line, /切换窗口间距/)) prio = 41
if (match(line, /切换夜间模式/)) prio = 42
if (match(line, /切换空闲锁定/)) prio = 43
if (match(line, /组内|分组/)) prio = 94
if (match(line, /工作区向(前|后)滚动/)) prio = 95
if (match(line, /Cycle to/)) prio = 96
if (match(line, /Reveal active/)) prio = 97
if (match(line, /置顶显示/)) prio = 97
if (match(line, /Apple Display/)) prio = 98
if (match(line, /XF86/)) prio = 99
if (match(line, /Tmux keybindings/)) prio = 100
if (match(line, /Herdr keybindings/)) prio = 101
if (match(line, /Tmux 快捷键/)) prio = 100
if (match(line, /Herdr 快捷键/)) prio = 101
# print "priority<TAB>record"
printf "%d\t%s\n", prio, $0
+1 -1
View File
@@ -186,7 +186,7 @@ tips=(
"Share files with phones and laptops using LocalSend"
"Turn any website into an app with Install > Web App in the menu"
"Super + Return opens a terminal, Super + Shift + Return the browser"
"Edit images with Pinta, videos with Kdenlive, docs with LibreOffice"
"Edit images with Pinta and videos with Kdenlive"
"Super + Ctrl + Print grabs text off the screen with OCR"
"Print takes a screenshot, Alt + Print records the screen"
"Set a reminder with Super + Ctrl + R"
+1 -2
View File
@@ -11,7 +11,7 @@ Usage: omarchy finalize user [--force] [--first-install]
Runs the per-user setup steps that /etc/skel can't seed:
dev-aware skill symlinks, xdg-user-dirs + gtk bookmarks (need \$HOME),
default browser/mailto, and install/user/all.sh.
default browser, and install/user/all.sh.
For shipped configs see /etc/skel (new users) and omarchy-reinstall-configs
(existing users explicitly resyncing).
@@ -110,7 +110,6 @@ source "$OMARCHY_INSTALL/user/all.sh"
omarchy-refresh-applications
env -u BROWSER xdg-settings set default-web-browser chromium.desktop
xdg-mime default HEY.desktop x-scheme-handler/mailto
if (( first_install )); then
mkdir -p "$state_dir/migrations"
-3
View File
@@ -18,13 +18,10 @@ if gum confirm "Are you sure you want to remove all preinstalled web apps, TUI w
~/.local/bin/omp ~/.local/bin/ori ~/.local/bin/grok ~/.local/bin/crush ~/.local/bin/ghui ~/.local/bin/hunk
omarchy-pkg-drop \
aether \
cliamp \
libreoffice-fresh \
xournalpp \
pinta \
obsidian \
obs-studio \
kdenlive \
moonlight-qt \
lazydocker \
+17 -8
View File
@@ -5,7 +5,10 @@
set -e
if ! id -nG "$USER" 2>/dev/null | grep -qw docker; then
# Ask about the configured groups, not this session's: right after enabling,
# sudoless Docker is on for the account even though the running session still
# needs a prompt, and this command is what turns it back off.
if omarchy-sudo-docker --configured; then
echo "Sudoless Docker is not enabled: $USER is not in the docker group."
exit 0
fi
@@ -13,13 +16,19 @@ fi
echo "Removing $USER from the docker group..."
sudo gpasswd -d "$USER" docker >/dev/null
# Group membership is fixed at login, so the running session keeps its docker
# access until it ends. Flag a reboot so omarchy-update-restart prompts for one
# (and the bar shows it pending); a plain log out and back in works too.
# Group membership is only re-read by a fresh session, and in practice logging
# out or newgrp isn't enough — only a reboot reliably applies it. Record it so a
# later `omarchy update` still prompts (omarchy-update-restart reads this), then
# offer to do it now.
omarchy-state set reboot-required
echo ""
echo "Sudoless Docker DISABLED. Reboot (or log out and back in) for the change to take effect."
echo "Docker access now goes through a polkit/sudo prompt again: the Docker TUI"
echo "(Super + Shift + D) and the Windows VM will ask when they need it, and the"
echo "plain 'docker' CLI runs under sudo."
echo "Sudoless Docker DISABLED. Docker access goes through a polkit/sudo prompt"
echo "again: the Docker TUI (Super + Shift + D) and the Windows VM ask when they"
echo "need it, and the plain 'docker' CLI runs under sudo. It takes effect after a reboot."
echo ""
# The migration reuses this command during 'omarchy update' and defers the
# reboot to omarchy-update-restart, so it doesn't cut the update short.
if [[ -z ${OMARCHY_DEFER_REBOOT:-} ]] && gum confirm "Reboot now to apply?"; then
omarchy-system-reboot
fi
+15 -7
View File
@@ -5,7 +5,9 @@
set -e
if id -nG "$USER" 2>/dev/null | grep -qw docker; then
# Ask about the configured groups, not this session's: once enabled it stays
# enabled for the account even before the reboot that lets this session use it.
if ! omarchy-sudo-docker --configured; then
echo "Sudoless Docker is already enabled: $USER is in the docker group."
echo "To disable it again, run: omarchy-remove-security-sudoless-docker"
exit 0
@@ -28,14 +30,20 @@ echo ""
if gum confirm "Enable sudoless Docker? This gives anything running as you passwordless root."; then
sudo usermod -aG docker "$USER"
# Group membership is fixed at login, so docker won't be reachable without a
# prompt until the session restarts. Flag a reboot so omarchy-update-restart
# prompts for one (and the bar shows it pending).
# A new docker group membership is only picked up by a fresh session, and in
# practice logging out or newgrp isn't enough — only a reboot reliably applies
# it. Record it so a later `omarchy update` still prompts
# (omarchy-update-restart reads this), then offer to do it now.
omarchy-state set reboot-required
echo ""
echo "Sudoless Docker ENABLED. Reboot, or log out and back in (or run 'newgrp docker'),"
echo "for the new group membership to take effect."
echo "To disable it again, run: omarchy-remove-security-sudoless-docker"
echo "Sudoless Docker ENABLED. It takes effect after a reboot."
echo "To disable it again: Setup > Security > Sudoless Docker."
echo ""
# The migration reuses this command during 'omarchy update' and defers the
# reboot to omarchy-update-restart, so it doesn't cut the update short.
if [[ -z ${OMARCHY_DEFER_REBOOT:-} ]] && gum confirm "Reboot now to apply?"; then
omarchy-system-reboot
fi
else
echo "Aborted. No changes made. Docker access still goes through a prompt."
fi
+44
View File
@@ -0,0 +1,44 @@
#!/bin/bash
# omarchy:summary=Succeed when Docker needs sudo, fail when it can be used directly
# omarchy:args=[--configured]
# omarchy:examples=omarchy-sudo-docker && echo "needs sudo" | omarchy-sudo-docker --configured
# omarchy:hidden=true
# The docker group is root-equivalent, so Omarchy leaves users out of it by
# default and reaches the daemon through a prompt instead. Everything that has
# to make that choice asks here rather than testing group membership itself.
#
# Two questions, because they have different answers between toggling sudoless
# Docker and the reboot that applies it (group membership is fixed when the
# session is created):
#
# (default) Does Docker need sudo *right now*? Answered by whether this
# process can actually reach the socket, which is what decides
# if a command must elevate. Still true in the window after
# sudoless Docker is enabled but before the reboot.
# --configured Will it need sudo once the account's groups take effect?
# Answered from the account's configured groups, so the menu
# offers the toggle that can actually change state.
#
# Succeeds (exit 0) when sudo is needed, so it reads as `if omarchy-sudo-docker`.
DOCKER_SOCKET="${OMARCHY_DOCKER_SOCKET:-/var/run/docker.sock}"
case "${1:-}" in
--configured)
# An account in the docker group will not need sudo after the next login.
id -nG "$USER" 2>/dev/null | grep -qw docker && exit 1
exit 0
;;
"")
# A socket we can write is a daemon we can drive without elevating. A missing
# socket counts as needing sudo: reaching it means starting it as root anyway.
[[ -w $DOCKER_SOCKET ]] && exit 1
exit 0
;;
*)
echo "Usage: omarchy-sudo-docker [--configured]" >&2
exit 2
;;
esac
+6 -6
View File
@@ -3,16 +3,16 @@
# omarchy:summary=Prompt for confirmation before starting an update
gum style --border normal --padding "1 2" \
"Ready to update?" \
"准备好更新了吗?" \
"" \
"• You cannot stop the update once you start!" \
"• Make sure you're connected to power or have a full battery" \
"• 更新一旦开始就不能中止!" \
"• 请确保已接通电源或电量充足" \
"" \
"What's new: https://github.com/basecamp/omarchy/releases/latest"
"更新内容: https://git.zacharyzhang.com/ZacharyZhang-NY/omarchycn/releases"
echo
if ! gum confirm "Continue with update?"; then
echo "Update cancelled"
if ! gum confirm "继续更新?"; then
echo "已取消更新"
exit 1
fi
-4
View File
@@ -2196,10 +2196,6 @@ fi
if command -v xdg-settings >/dev/null 2>&1; then
env -u BROWSER xdg-settings set default-web-browser chromium.desktop || true
fi
if command -v xdg-mime >/dev/null 2>&1; then
xdg-mime default HEY.desktop x-scheme-handler/mailto || true
fi
# This script is fetched from the branch and runs against whatever packaged
# tree the channel currently serves, so the packaged tree can be older than the
# script. The completion markers are written directly rather than through
-15
View File
@@ -1,15 +0,0 @@
#!/bin/bash
# omarchy:summary=Open HEY webmail and translate mailto links
# omarchy:args=[url]
url="$1"
web_url="https://app.hey.com"
# Handle mailto: URLs
if [[ $url =~ ^mailto: ]]; then
email=$(echo "$url" | sed 's/mailto://')
web_url="https://app.hey.com/messages/new?to=$email"
fi
exec omarchy-launch-webapp "$web_url"
+6 -3
View File
@@ -31,8 +31,11 @@ CONTAINER="omarchy-windows"
# --- privilege helpers -------------------------------------------------------
# True when the user can reach the Docker socket directly (sudoless Docker on).
in_docker_group() { id -nG 2>/dev/null | grep -qw docker; }
# True when this session can reach the Docker socket directly (sudoless Docker
# on and in effect). Asking about the socket rather than the configured groups
# keeps the prompt in place through the window where sudoless Docker is enabled
# but the reboot that grants the group has not happened yet.
docker_needs_sudo() { omarchy-sudo-docker; }
# The command to hand pkexec for the privileged re-exec. pkexec runs whatever
# executable it is given (after authorization) and only shows the path in the
@@ -64,7 +67,7 @@ priv_target() {
priv() {
local action="$1"
shift
if [[ $action != write_compose ]] && in_docker_group; then
if [[ $action != write_compose ]] && ! docker_needs_sudo; then
"__priv_$action" "$@"
return
fi
+6 -1
View File
@@ -1,5 +1,5 @@
{
"_verified": "2026-08-24. level=stable: adapter 渲染由 test/shell.d/omarchycn-ai-test.sh mock 回归覆盖; level=official-doc: 官方集成文档存在,native CLI 自管配置",
"_verified": "2026-08-25. level=stable: adapter 渲染由 test/shell.d/omarchycn-ai-test.sh mock 回归覆盖; level=official-doc: 官方集成文档存在,native CLI 自管配置",
"combos": {
"claude-code": {
"deepseek": {
@@ -16,6 +16,11 @@
"level": "stable",
"doc": "https://docs.z.ai/devpack/tool/claude",
"adapter": true
},
"minimax": {
"level": "stable",
"doc": "https://platform.minimax.io/docs/token-plan/claude-code",
"adapter": true
}
},
"codex": {
+2 -2
View File
@@ -1,5 +1,5 @@
{
"_verified": "2026-08-24",
"_verified": "2026-08-25",
"harnesses": {
"claude-code": {
"display_name": "Claude Code",
@@ -25,7 +25,7 @@
"kimi-code": {
"display_name": "Kimi Code CLI",
"command": "kimi",
"install": "doc:https://www.kimi.com/code/docs/en/",
"install": "omarchy-cn-kimi-install",
"config_method": "native",
"protocol": "native"
},
+118 -12
View File
@@ -1,44 +1,150 @@
{
"_verified": "2026-08-24, from official docs (see OmarchyCN PRD §27)",
"_verified": "2026-08-25, from official docs (see OmarchyCN PRD §27; minimax: platform.minimax.io/docs/token-plan/claude-code)",
"providers": {
"deepseek": {
"display_name": "DeepSeek",
"auth": ["api_key"],
"auth": [
"api_key"
],
"key_url": "https://platform.deepseek.com/api_keys",
"endpoints": {
"anthropic": "https://api.deepseek.com/anthropic",
"openai": "https://api.deepseek.com"
},
"models": [
{ "id": "deepseek-v4-pro[1m]", "aliases": ["default-coding"], "capabilities": ["tools", "streaming", "reasoning"] },
{ "id": "deepseek-v4-flash", "aliases": ["fast"], "capabilities": ["tools", "streaming"] }
{
"id": "deepseek-v4-pro[1m]",
"aliases": [
"default-coding"
],
"capabilities": [
"tools",
"streaming",
"reasoning"
]
},
{
"id": "deepseek-v4-flash",
"aliases": [
"fast"
],
"capabilities": [
"tools",
"streaming"
]
}
]
},
"kimi": {
"display_name": "Kimi (Moonshot)",
"auth": ["api_key"],
"auth": [
"api_key"
],
"key_url": "https://www.kimi.com/code",
"endpoints": {
"anthropic": "https://api.kimi.com/coding/",
"openai": "https://api.moonshot.cn/v1"
},
"models": [
{ "id": "kimi-for-coding", "aliases": ["default-coding"], "capabilities": ["tools", "streaming", "reasoning"] },
{ "id": "kimi-for-coding-highspeed", "aliases": ["fast"], "capabilities": ["tools", "streaming"] },
{ "id": "kimi-k2.6", "aliases": ["general"], "capabilities": ["tools", "streaming"], "protocol": "openai" }
{
"id": "kimi-for-coding",
"aliases": [
"default-coding"
],
"capabilities": [
"tools",
"streaming",
"reasoning"
]
},
{
"id": "kimi-for-coding-highspeed",
"aliases": [
"fast"
],
"capabilities": [
"tools",
"streaming"
]
},
{
"id": "kimi-k2.6",
"aliases": [
"general"
],
"capabilities": [
"tools",
"streaming"
],
"protocol": "openai"
}
]
},
"zai": {
"display_name": "Z.AI / GLM",
"auth": ["api_key"],
"auth": [
"api_key"
],
"key_url": "https://z.ai/manage-apikey/apikey-list",
"endpoints": {
"anthropic": "https://api.z.ai/api/anthropic"
},
"models": [
{ "id": "glm-5.2[1m]", "aliases": ["default-coding"], "capabilities": ["tools", "streaming", "reasoning"] },
{ "id": "GLM-5.3", "aliases": ["latest"], "capabilities": ["tools", "streaming", "reasoning"] },
{ "id": "GLM-4.7", "aliases": ["fast"], "capabilities": ["tools", "streaming"] }
{
"id": "glm-5.2[1m]",
"aliases": [
"default-coding"
],
"capabilities": [
"tools",
"streaming",
"reasoning"
]
},
{
"id": "GLM-5.3",
"aliases": [
"latest"
],
"capabilities": [
"tools",
"streaming",
"reasoning"
]
},
{
"id": "GLM-4.7",
"aliases": [
"fast"
],
"capabilities": [
"tools",
"streaming"
]
}
]
},
"minimax": {
"display_name": "MiniMax",
"auth": [
"api_key"
],
"key_url": "https://platform.minimaxi.com/user-center/payment/token-plan",
"endpoints": {
"anthropic": "https://api.minimaxi.com/anthropic"
},
"models": [
{
"id": "MiniMax-M3[1m]",
"aliases": [
"default-coding"
],
"capabilities": [
"tools",
"streaming",
"reasoning"
]
}
]
}
}
+1 -1
View File
@@ -1 +1 @@
2
5
+1 -1
View File
@@ -2,4 +2,4 @@
--ozone-platform-hint=wayland
--password-store=gnome-libsecret
--enable-features=TouchpadOverscrollHistoryNavigation
--load-extension=/usr/share/omarchy/default/chromium/extensions/copy-url,/usr/share/omarchy/default/chromium/extensions/yt-dlp,/usr/share/omarchy/default/chromium/extensions/whatsapp-slim
--load-extension=/usr/share/omarchy/default/chromium/extensions/copy-url,/usr/share/omarchy/default/chromium/extensions/yt-dlp
-1
View File
@@ -12,7 +12,6 @@ application/pdf=org.gnome.Evince.desktop
x-scheme-handler/http=chromium.desktop
x-scheme-handler/https=chromium.desktop
x-scheme-handler/mailto=HEY.desktop
video/mp4=mpv.desktop
video/x-msvideo=mpv.desktop
@@ -1,21 +0,0 @@
{
"manifest_version": 3,
"name": "WhatsApp Slim",
"version": "1.1",
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAszLhFYxjvKqx1VAORt3M6b0H1Kz03cmQ1MrUjJZ8gj3nhRcvufk6PS17yUIjEdKKmuj+1HuFVwOaHJQsACGJXwn2TeKQqZmZx/83yd7ydgabHXTsJbhy5O+Yunv+VA1B2IX/phcXpkH9bPv2PuC/bQ13nmmAvT2b+EMyq8EPQHOoSWhE4M4kIhLDczTmxfJX5X3tTxH66952DYuQWvGpIXLAUXkvIgap0qemW68/LymPaWzcg6g3oE/nZ7C0j7JE5bnqLFxqXNlA9Jt+Ss/1Y33QL3OSD9EEsrpgueS0bbqtkmFQ9/jGlruIL+qj0xe+CAtqbJ5ty9H/sqi01kjfyQIDAQAB",
"description": "Make WhatsApp Web follow the system theme and collapse its chat list in narrow windows, this extension is installed by Omarchy",
"content_scripts": [
{
"matches": [
"https://web.whatsapp.com/*"
],
"css": [
"whatsapp.css"
],
"js": [
"system-theme.js"
],
"run_at": "document_start"
}
]
}
@@ -1,4 +0,0 @@
if (localStorage.getItem("system-theme-mode") !== "true") {
localStorage.setItem("system-theme-mode", "true");
location.reload();
}
@@ -1,64 +0,0 @@
/* Collapse WhatsApp Web's chat list to a Signal-style avatar rail when the
* window is too narrow to show both the full list and the conversation.
* Leans on WhatsApp's stable structural ids (#side, #main, #pane-side) and
* data-testid hooks rather than its obfuscated, frequently-churning class
* names. */
@media (max-width: 1100px) {
/* The chat-list column (title header + #side) becomes an avatar rail */
div:has(> div#side) {
flex: 0 0 90px !important;
min-width: 90px !important;
max-width: 90px !important;
}
/* Chat-list title bar, search, filter chips, and banner don't fit.
* Scoped to the column wrapper: the left icon nav rail confusingly
* shares the same chatlist-header testid and must survive. */
div:has(> div#side) > header,
#side > div:first-child,
#side [role="tablist"],
#side span[data-testid="chat-butterbar"] {
display: none !important;
}
/* The rail doesn't need a visible scrollbar, and horizontal overflow
* from the collapsed rows must not wiggle */
#pane-side {
overflow-x: hidden !important;
scrollbar-width: none !important;
}
/* Chat rows: keep the avatar column, collapse the text/preview columns
* without display:none so the unread badge can be resurrected */
[data-testid="cell-frame-container"] {
position: relative !important;
}
[data-testid="cell-frame-container"] > div:not(:first-child) {
visibility: hidden !important;
width: 0 !important;
min-width: 0 !important;
overflow: hidden !important;
}
/* Float the unread badge over the avatar's top-right corner */
[data-testid="cell-frame-container"] span[aria-label*="unread"] {
visibility: visible !important;
position: absolute !important;
top: 2px !important;
left: 50px !important;
}
/* The empty drawer overlay keeps the stock chat-list width, which
* strands its divider border in the middle of the conversation */
[data-testid="drawer-middle"] {
border-left: none !important;
}
/* Let the app shrink below WhatsApp's default minimum width */
#app,
#app .two {
min-width: 0 !important;
}
}
+10 -19
View File
@@ -1,34 +1,25 @@
-- Essential application bindings.
o.bind("SUPER + RETURN", "Terminal", { omarchy = "terminal" })
o.bind("SUPER + SHIFT + RETURN", "Browser", { omarchy = "browser" })
o.bind("SUPER + SHIFT + F", "File manager", { omarchy = "nautilus" })
o.bind("SUPER + ALT + SHIFT + F", "File manager (cwd)", { omarchy = "nautilus-cwd" })
o.bind("SUPER + SHIFT + B", "Browser", { omarchy = "browser" })
o.bind("SUPER + SHIFT + ALT + B", "Browser (private)", { omarchy = "browser --private" })
o.bind("SUPER + SHIFT + N", "Editor", { omarchy = "editor" })
o.bind("SUPER + RETURN", "终端", { omarchy = "terminal" })
o.bind("SUPER + SHIFT + RETURN", "浏览器", { omarchy = "browser" })
o.bind("SUPER + SHIFT + F", "文件管理器", { omarchy = "nautilus" })
o.bind("SUPER + ALT + SHIFT + F", "文件管理器(当前目录)", { omarchy = "nautilus-cwd" })
o.bind("SUPER + SHIFT + B", "浏览器", { omarchy = "browser" })
o.bind("SUPER + SHIFT + ALT + B", "浏览器(无痕)", { omarchy = "browser --private" })
o.bind("SUPER + SHIFT + N", "编辑器", { omarchy = "editor" })
if o.preinstalled_bindings_enabled() then
-- Bindings for preinstalled Omarchy applications, TUIs, and web apps.
o.bind("SUPER + ALT + RETURN", "Tmux", { omarchy = "terminal-tmux" })
o.bind("SUPER + CTRL + RETURN", "Herdr", { omarchy = "terminal-herdr" })
o.bind("SUPER + SHIFT + M", "Music", { omarchy = "spotify" })
o.bind("SUPER + SHIFT + ALT + M", "Music TUI", { tui = "cliamp", focus = true })
o.bind("SUPER + SHIFT + M", "音乐", { omarchy = "spotify" })
o.bind("SUPER + SHIFT + ALT + M", "音乐 TUI", { tui = "cliamp", focus = true })
o.bind("SUPER + SHIFT + D", "Docker", { tui = "omarchy-launch-docker-tui" })
o.bind("SUPER + SHIFT + G", "Signal", { omarchy = "signal" })
o.bind("SUPER + SHIFT + O", "Obsidian", { launch = "obsidian", focus = "^obsidian$" })
o.bind("SUPER + SHIFT + W", "Omawrite", { launch = "omawrite" })
o.bind("SUPER + SHIFT + SLASH", "Passwords", { omarchy = "1password" })
o.bind("SUPER + SHIFT + SLASH", "密码管理", { omarchy = "1password" })
o.bind("SUPER + SHIFT + A", "ChatGPT", { webapp = "https://chatgpt.com" })
o.bind("SUPER + SHIFT + ALT + A", "Grok", { webapp = "https://grok.com" })
o.bind("SUPER + SHIFT + C", "Calendar", { webapp = "https://app.hey.com/calendar/weeks/" })
o.bind("SUPER + SHIFT + E", "Email", { webapp = "https://app.hey.com" })
o.bind("SUPER + SHIFT + ALT + E", "New email", { webapp = "https://app.hey.com/messages/new?display=standalone&new_window=true" })
o.bind("SUPER + SHIFT + Y", "YouTube", { webapp = "https://youtube.com/" })
o.bind("SUPER + SHIFT + ALT + G", "WhatsApp", { webapp = "https://web.whatsapp.com/", focus = true })
o.bind( "SUPER + SHIFT + CTRL + G", "Google Messages", { webapp = "https://messages.google.com/web/conversations", focus = true })
o.bind("SUPER + SHIFT + P", "Google Photos", { webapp = "https://photos.google.com/", focus = true })
o.bind("SUPER + SHIFT + S", "Google Maps", { webapp = "https://maps.google.com/", focus = true })
o.bind("SUPER + SHIFT + X", "X", { webapp = "https://x.com/" })
o.bind("SUPER + SHIFT + ALT + X", "X Post", { webapp = "https://x.com/compose/post" })
end
+4 -4
View File
@@ -42,7 +42,7 @@ local function universal_clipboard_shortcut(default_mods, default_key, terminal_
end
end
o.bind("SUPER + C", "Universal copy", universal_clipboard_shortcut("CTRL", "C", "CTRL", "Insert"))
o.bind("SUPER + V", "Universal paste", universal_clipboard_shortcut("CTRL", "V", "SHIFT", "Insert"))
o.bind("SUPER + X", "Universal cut", send_shortcut_once("CTRL", "X"))
o.bind("SUPER + CTRL + V", "Clipboard manager", "omarchy-shell shell toggle omarchy.clipboard")
o.bind("SUPER + C", "通用复制", universal_clipboard_shortcut("CTRL", "C", "CTRL", "Insert"))
o.bind("SUPER + V", "通用粘贴", universal_clipboard_shortcut("CTRL", "V", "SHIFT", "Insert"))
o.bind("SUPER + X", "通用剪切", send_shortcut_once("CTRL", "X"))
o.bind("SUPER + CTRL + V", "剪贴板管理器", "omarchy-shell shell toggle omarchy.clipboard")
+28 -28
View File
@@ -1,34 +1,34 @@
-- Volume, brightness, keyboard backlight, and touchpad controls.
o.bind("XF86AudioRaiseVolume", "Volume up", "omarchy-audio-output-volume raise", { locked = true, repeating = true })
o.bind("XF86AudioLowerVolume", "Volume down", "omarchy-audio-output-volume lower", { locked = true, repeating = true })
o.bind("XF86AudioMute", "Mute", "omarchy-audio-output-volume mute-toggle", { locked = true })
o.bind("XF86AudioMicMute", "Mute microphone", "omarchy-audio-input-mute", { locked = true })
o.bind("XF86MonBrightnessUp", "Brightness up", "omarchy-brightness-display +5%", { locked = true, repeating = true })
o.bind("XF86MonBrightnessDown", "Brightness down", "omarchy-brightness-display 5%-", { locked = true, repeating = true })
o.bind("SHIFT + XF86MonBrightnessUp", "Brightness maximum", "omarchy-brightness-display 100%", { locked = true, repeating = true })
o.bind("SHIFT + XF86MonBrightnessDown", "Brightness minimum", "omarchy-brightness-display 1%", { locked = true, repeating = true })
o.bind("XF86KbdBrightnessUp", "Keyboard brightness up", "omarchy-brightness-keyboard up", { locked = true, repeating = true })
o.bind("XF86KbdBrightnessDown", "Keyboard brightness down", "omarchy-brightness-keyboard down", { locked = true, repeating = true })
o.bind("XF86KbdLightOnOff", "Keyboard backlight cycle", "omarchy-brightness-keyboard cycle", { locked = true })
o.bind_toggle("XF86TouchpadToggle", "Toggle touchpad", "touchpad", { locked = true })
o.bind("XF86TouchpadOn", "Enable touchpad", "omarchy-toggle-touchpad on", { locked = true })
o.bind("XF86TouchpadOff", "Disable touchpad", "omarchy-toggle-touchpad off", { locked = true })
o.bind("XF86AudioRaiseVolume", "提高音量", "omarchy-audio-output-volume raise", { locked = true, repeating = true })
o.bind("XF86AudioLowerVolume", "降低音量", "omarchy-audio-output-volume lower", { locked = true, repeating = true })
o.bind("XF86AudioMute", "静音", "omarchy-audio-output-volume mute-toggle", { locked = true })
o.bind("XF86AudioMicMute", "麦克风静音", "omarchy-audio-input-mute", { locked = true })
o.bind("XF86MonBrightnessUp", "提高亮度", "omarchy-brightness-display +5%", { locked = true, repeating = true })
o.bind("XF86MonBrightnessDown", "降低亮度", "omarchy-brightness-display 5%-", { locked = true, repeating = true })
o.bind("SHIFT + XF86MonBrightnessUp", "最大亮度", "omarchy-brightness-display 100%", { locked = true, repeating = true })
o.bind("SHIFT + XF86MonBrightnessDown", "最小亮度", "omarchy-brightness-display 1%", { locked = true, repeating = true })
o.bind("XF86KbdBrightnessUp", "键盘背光提高", "omarchy-brightness-keyboard up", { locked = true, repeating = true })
o.bind("XF86KbdBrightnessDown", "键盘背光降低", "omarchy-brightness-keyboard down", { locked = true, repeating = true })
o.bind("XF86KbdLightOnOff", "键盘背光循环", "omarchy-brightness-keyboard cycle", { locked = true })
o.bind_toggle("XF86TouchpadToggle", "切换触控板", "touchpad", { locked = true })
o.bind("XF86TouchpadOn", "启用触控板", "omarchy-toggle-touchpad on", { locked = true })
o.bind("XF86TouchpadOff", "禁用触控板", "omarchy-toggle-touchpad off", { locked = true })
-- Precise volume and brightness controls.
o.bind("ALT + XF86AudioRaiseVolume", "Volume up precise", "omarchy-audio-output-volume +1", { locked = true, repeating = true })
o.bind("ALT + XF86AudioLowerVolume", "Volume down precise", "omarchy-audio-output-volume -1", { locked = true, repeating = true })
o.bind("ALT + XF86MonBrightnessUp", "Brightness up precise", "omarchy-brightness-display +1%", { locked = true, repeating = true })
o.bind("ALT + XF86MonBrightnessDown", "Brightness down precise", "omarchy-brightness-display 1%-", { locked = true, repeating = true })
o.bind("ALT + XF86AudioRaiseVolume", "微调提高音量", "omarchy-audio-output-volume +1", { locked = true, repeating = true })
o.bind("ALT + XF86AudioLowerVolume", "微调降低音量", "omarchy-audio-output-volume -1", { locked = true, repeating = true })
o.bind("ALT + XF86MonBrightnessUp", "微调提高亮度", "omarchy-brightness-display +1%", { locked = true, repeating = true })
o.bind("ALT + XF86MonBrightnessDown", "微调降低亮度", "omarchy-brightness-display 1%-", { locked = true, repeating = true })
-- Media controls.
o.bind("XF86AudioNext", "Next track", "omarchy-shell media next", { locked = true })
o.bind("ALT + XF86AudioPlay", "Next track", "omarchy-shell media next", { locked = true })
o.bind("XF86AudioPause", "Pause", "omarchy-shell media playPause", { locked = true })
o.bind("XF86AudioPlay", "Play", "omarchy-shell media playPause", { locked = true })
o.bind("XF86AudioPrev", "Previous track", "omarchy-shell media previous", { locked = true })
o.bind("ALT + SHIFT + XF86AudioPlay", "Previous track", "omarchy-shell media previous", { locked = true })
o.bind("XF86Eject", "Eject media", "eject", { locked = true })
o.bind("XF86AudioNext", "下一曲", "omarchy-shell media next", { locked = true })
o.bind("ALT + XF86AudioPlay", "下一曲", "omarchy-shell media next", { locked = true })
o.bind("XF86AudioPause", "暂停", "omarchy-shell media playPause", { locked = true })
o.bind("XF86AudioPlay", "播放", "omarchy-shell media playPause", { locked = true })
o.bind("XF86AudioPrev", "上一曲", "omarchy-shell media previous", { locked = true })
o.bind("ALT + SHIFT + XF86AudioPlay", "上一曲", "omarchy-shell media previous", { locked = true })
o.bind("XF86Eject", "弹出介质", "eject", { locked = true })
o.bind("SHIFT + XF86AudioMute", "Switch audio output", "omarchy-audio-output-switch", { locked = true })
o.bind("SHIFT + XF86AudioPause", "Switch media source", "omarchy-audio-source-switch", { locked = true })
o.bind("SHIFT + XF86AudioPlay", "Switch media source", "omarchy-audio-source-switch", { locked = true })
o.bind("SHIFT + XF86AudioMute", "切换音频输出", "omarchy-audio-output-switch", { locked = true })
o.bind("SHIFT + XF86AudioPause", "切换媒体源", "omarchy-audio-source-switch", { locked = true })
o.bind("SHIFT + XF86AudioPlay", "切换媒体源", "omarchy-audio-source-switch", { locked = true })
+72 -72
View File
@@ -1,98 +1,98 @@
o.bind("SUPER + W", "Close window", hl.dsp.window.close())
o.bind("SUPER + Q", "Close window", hl.dsp.window.close())
o.bind("CTRL + ALT + DELETE", "Close all windows", "omarchy-hyprland-window-close-all")
o.bind("SUPER + W", "关闭窗口", hl.dsp.window.close())
o.bind("SUPER + Q", "关闭窗口", hl.dsp.window.close())
o.bind("CTRL + ALT + DELETE", "关闭所有窗口", "omarchy-hyprland-window-close-all")
o.bind("SUPER + J", "Toggle window split", hl.dsp.layout("togglesplit"))
o.bind("SUPER + P", "Pseudo window", hl.dsp.window.pseudo())
o.bind("SUPER + T", "Toggle window floating/tiling", hl.dsp.window.float({ action = "toggle" }))
o.bind("SUPER + F", "Full screen", hl.dsp.window.fullscreen({ mode = "fullscreen" }))
o.bind("SUPER + CTRL + F", "Tiled full screen", "omarchy-hyprland-window-tiled-fullscreen-toggle")
o.bind("SUPER + ALT + F", "Full width", hl.dsp.window.fullscreen({ mode = "maximized" }))
o.bind("SUPER + O", "Pop window out (float & pin)", "omarchy-hyprland-window-pop")
o.bind("SUPER + ALT + Home", "Save window width", "omarchy-hyprland-window-width save")
o.bind("SUPER + Home", "Restore window width", "omarchy-hyprland-window-width restore")
o.bind("SUPER + L", "Toggle workspace layout", "omarchy-hyprland-workspace-layout-toggle")
o.bind("SUPER + J", "切换窗口分割", hl.dsp.layout("togglesplit"))
o.bind("SUPER + P", "伪平铺窗口", hl.dsp.window.pseudo())
o.bind("SUPER + T", "切换窗口浮动/平铺", hl.dsp.window.float({ action = "toggle" }))
o.bind("SUPER + F", "全屏", hl.dsp.window.fullscreen({ mode = "fullscreen" }))
o.bind("SUPER + CTRL + F", "平铺全屏", "omarchy-hyprland-window-tiled-fullscreen-toggle")
o.bind("SUPER + ALT + F", "全宽", hl.dsp.window.fullscreen({ mode = "maximized" }))
o.bind("SUPER + O", "弹出窗口(浮动并置顶)", "omarchy-hyprland-window-pop")
o.bind("SUPER + ALT + Home", "保存窗口宽度", "omarchy-hyprland-window-width save")
o.bind("SUPER + Home", "恢复窗口宽度", "omarchy-hyprland-window-width restore")
o.bind("SUPER + L", "切换工作区布局", "omarchy-hyprland-workspace-layout-toggle")
o.bind("SUPER + LEFT", "Focus on left window", hl.dsp.focus({ direction = "l" }))
o.bind("SUPER + RIGHT", "Focus on right window", hl.dsp.focus({ direction = "r" }))
o.bind("SUPER + UP", "Focus on above window", hl.dsp.focus({ direction = "u" }))
o.bind("SUPER + DOWN", "Focus on below window", hl.dsp.focus({ direction = "d" }))
o.bind("SUPER + LEFT", "聚焦左侧窗口", hl.dsp.focus({ direction = "l" }))
o.bind("SUPER + RIGHT", "聚焦右侧窗口", hl.dsp.focus({ direction = "r" }))
o.bind("SUPER + UP", "聚焦上方窗口", hl.dsp.focus({ direction = "u" }))
o.bind("SUPER + DOWN", "聚焦下方窗口", hl.dsp.focus({ direction = "d" }))
for workspace = 1, 10 do
local key = "code:" .. tostring(workspace + 9)
o.bind("SUPER + " .. key, "Switch to workspace " .. workspace, hl.dsp.focus({ workspace = tostring(workspace) }))
o.bind("SUPER + SHIFT + " .. key, "Move window to workspace " .. workspace, hl.dsp.window.move({ workspace = tostring(workspace) }))
o.bind("SUPER + SHIFT + ALT + " .. key, "Move window silently to workspace " .. workspace, hl.dsp.window.move({ workspace = tostring(workspace), follow = false }))
o.bind("SUPER + " .. key, "切换到工作区 " .. workspace, hl.dsp.focus({ workspace = tostring(workspace) }))
o.bind("SUPER + SHIFT + " .. key, "窗口移到工作区 " .. workspace, hl.dsp.window.move({ workspace = tostring(workspace) }))
o.bind("SUPER + SHIFT + ALT + " .. key, "窗口静默移到工作区 " .. workspace, hl.dsp.window.move({ workspace = tostring(workspace), follow = false }))
end
o.bind("SUPER + S", "Toggle scratchpad", hl.dsp.workspace.toggle_special("scratchpad"))
o.bind("SUPER + ALT + S", "Move window to scratchpad", hl.dsp.window.move({ workspace = "special:scratchpad", follow = false }))
o.bind("SUPER + grave", "Toggle scratchpad", hl.dsp.workspace.toggle_special("scratchpad"))
o.bind("SUPER + SHIFT + grave", "Move window to scratchpad", hl.dsp.window.move({ workspace = "special:scratchpad", follow = false }))
o.bind("SUPER + S", "切换便签区", hl.dsp.workspace.toggle_special("scratchpad"))
o.bind("SUPER + ALT + S", "窗口移入便签区", hl.dsp.window.move({ workspace = "special:scratchpad", follow = false }))
o.bind("SUPER + grave", "切换便签区", hl.dsp.workspace.toggle_special("scratchpad"))
o.bind("SUPER + SHIFT + grave", "窗口移入便签区", hl.dsp.window.move({ workspace = "special:scratchpad", follow = false }))
o.bind("SUPER + TAB", "Next workspace", hl.dsp.focus({ workspace = "e+1" }))
o.bind("SUPER + SHIFT + TAB", "Previous workspace", hl.dsp.focus({ workspace = "e-1" }))
o.bind("SUPER + CTRL + TAB", "Former workspace", hl.dsp.focus({ workspace = "previous" }))
o.bind("SUPER + TAB", "下一工作区", hl.dsp.focus({ workspace = "e+1" }))
o.bind("SUPER + SHIFT + TAB", "上一工作区", hl.dsp.focus({ workspace = "e-1" }))
o.bind("SUPER + CTRL + TAB", "上次工作区", hl.dsp.focus({ workspace = "previous" }))
o.bind("SUPER + SHIFT + ALT + LEFT", "Move workspace to left monitor", hl.dsp.workspace.move({ monitor = "l" }))
o.bind("SUPER + SHIFT + ALT + RIGHT", "Move workspace to right monitor", hl.dsp.workspace.move({ monitor = "r" }))
o.bind("SUPER + SHIFT + ALT + UP", "Move workspace to up monitor", hl.dsp.workspace.move({ monitor = "u" }))
o.bind("SUPER + SHIFT + ALT + DOWN", "Move workspace to down monitor", hl.dsp.workspace.move({ monitor = "d" }))
o.bind("SUPER + SHIFT + ALT + LEFT", "工作区移到左侧显示器", hl.dsp.workspace.move({ monitor = "l" }))
o.bind("SUPER + SHIFT + ALT + RIGHT", "工作区移到右侧显示器", hl.dsp.workspace.move({ monitor = "r" }))
o.bind("SUPER + SHIFT + ALT + UP", "工作区移到上方显示器", hl.dsp.workspace.move({ monitor = "u" }))
o.bind("SUPER + SHIFT + ALT + DOWN", "工作区移到下方显示器", hl.dsp.workspace.move({ monitor = "d" }))
o.bind("SUPER + SHIFT + LEFT", "Swap window to the left", hl.dsp.window.swap({ direction = "l" }))
o.bind("SUPER + SHIFT + RIGHT", "Swap window to the right", hl.dsp.window.swap({ direction = "r" }))
o.bind("SUPER + SHIFT + UP", "Swap window up", hl.dsp.window.swap({ direction = "u" }))
o.bind("SUPER + SHIFT + DOWN", "Swap window down", hl.dsp.window.swap({ direction = "d" }))
o.bind("SUPER + SHIFT + LEFT", "窗口左移互换", hl.dsp.window.swap({ direction = "l" }))
o.bind("SUPER + SHIFT + RIGHT", "窗口右移互换", hl.dsp.window.swap({ direction = "r" }))
o.bind("SUPER + SHIFT + UP", "窗口上移互换", hl.dsp.window.swap({ direction = "u" }))
o.bind("SUPER + SHIFT + DOWN", "窗口下移互换", hl.dsp.window.swap({ direction = "d" }))
o.bind("ALT + TAB", "Focus on next window", hl.dsp.window.cycle_next())
o.bind("ALT + SHIFT + TAB", "Focus on previous window", hl.dsp.window.cycle_next({ next = false }))
o.bind("ALT + TAB", "Reveal active window on top", hl.dsp.window.bring_to_top())
o.bind("ALT + SHIFT + TAB", "Reveal active window on top", hl.dsp.window.bring_to_top())
o.bind("ALT + TAB", "聚焦下一窗口", hl.dsp.window.cycle_next())
o.bind("ALT + SHIFT + TAB", "聚焦上一窗口", hl.dsp.window.cycle_next({ next = false }))
o.bind("ALT + TAB", "活动窗口置顶显示", hl.dsp.window.bring_to_top())
o.bind("ALT + SHIFT + TAB", "活动窗口置顶显示", hl.dsp.window.bring_to_top())
o.bind("CTRL + ALT + TAB", "Focus on next monitor", hl.dsp.focus({ monitor = "+1" }))
o.bind("CTRL + ALT + SHIFT + TAB", "Focus on previous monitor", hl.dsp.focus({ monitor = "-1" }))
o.bind("CTRL + ALT + TAB", "聚焦下一显示器", hl.dsp.focus({ monitor = "+1" }))
o.bind("CTRL + ALT + SHIFT + TAB", "聚焦上一显示器", hl.dsp.focus({ monitor = "-1" }))
o.bind("SUPER + code:20", "Expand window left", hl.dsp.window.resize({ x = -100, y = 0, relative = true }))
o.bind("SUPER + code:21", "Shrink window left", hl.dsp.window.resize({ x = 100, y = 0, relative = true }))
o.bind("SUPER + SHIFT + code:20", "Shrink window up", hl.dsp.window.resize({ x = 0, y = -100, relative = true }))
o.bind("SUPER + SHIFT + code:21", "Expand window down", hl.dsp.window.resize({ x = 0, y = 100, relative = true }))
o.bind("SUPER + code:20", "向左扩展窗口", hl.dsp.window.resize({ x = -100, y = 0, relative = true }))
o.bind("SUPER + code:21", "向左收缩窗口", hl.dsp.window.resize({ x = 100, y = 0, relative = true }))
o.bind("SUPER + SHIFT + code:20", "向上收缩窗口", hl.dsp.window.resize({ x = 0, y = -100, relative = true }))
o.bind("SUPER + SHIFT + code:21", "向下扩展窗口", hl.dsp.window.resize({ x = 0, y = 100, relative = true }))
o.bind("SUPER + ALT + code:20", "Expand window left a little", hl.dsp.window.resize({ x = -25, y = 0, relative = true }))
o.bind("SUPER + ALT + code:21", "Shrink window left a little", hl.dsp.window.resize({ x = 25, y = 0, relative = true }))
o.bind("SUPER + SHIFT + ALT + code:20", "Shrink window up a little", hl.dsp.window.resize({ x = 0, y = -25, relative = true }))
o.bind("SUPER + SHIFT + ALT + code:21", "Expand window down a little", hl.dsp.window.resize({ x = 0, y = 25, relative = true }))
o.bind("SUPER + ALT + code:20", "向左略微扩展窗口", hl.dsp.window.resize({ x = -25, y = 0, relative = true }))
o.bind("SUPER + ALT + code:21", "向左略微收缩窗口", hl.dsp.window.resize({ x = 25, y = 0, relative = true }))
o.bind("SUPER + SHIFT + ALT + code:20", "向上略微收缩窗口", hl.dsp.window.resize({ x = 0, y = -25, relative = true }))
o.bind("SUPER + SHIFT + ALT + code:21", "向下略微扩展窗口", hl.dsp.window.resize({ x = 0, y = 25, relative = true }))
o.bind("SUPER + CTRL + code:20", "Expand window left a lot", hl.dsp.window.resize({ x = -300, y = 0, relative = true }))
o.bind("SUPER + CTRL + code:21", "Shrink window left a lot", hl.dsp.window.resize({ x = 300, y = 0, relative = true }))
o.bind("SUPER + CTRL + SHIFT + code:20", "Shrink window up a lot", hl.dsp.window.resize({ x = 0, y = -300, relative = true }))
o.bind("SUPER + CTRL + SHIFT + code:21", "Expand window down a lot", hl.dsp.window.resize({ x = 0, y = 300, relative = true }))
o.bind("SUPER + CTRL + code:20", "向左大幅扩展窗口", hl.dsp.window.resize({ x = -300, y = 0, relative = true }))
o.bind("SUPER + CTRL + code:21", "向左大幅收缩窗口", hl.dsp.window.resize({ x = 300, y = 0, relative = true }))
o.bind("SUPER + CTRL + SHIFT + code:20", "向上大幅收缩窗口", hl.dsp.window.resize({ x = 0, y = -300, relative = true }))
o.bind("SUPER + CTRL + SHIFT + code:21", "向下大幅扩展窗口", hl.dsp.window.resize({ x = 0, y = 300, relative = true }))
o.bind("SUPER + mouse_down", "Scroll active workspace forward", hl.dsp.focus({ workspace = "e+1" }))
o.bind("SUPER + mouse_up", "Scroll active workspace backward", hl.dsp.focus({ workspace = "e-1" }))
o.bind("SUPER + mouse_down", "工作区向前滚动", hl.dsp.focus({ workspace = "e+1" }))
o.bind("SUPER + mouse_up", "工作区向后滚动", hl.dsp.focus({ workspace = "e-1" }))
o.bind("SUPER + mouse:272", "Move window", hl.dsp.window.drag(), { mouse = true })
o.bind("SUPER + mouse:273", "Resize window", hl.dsp.window.resize(), { mouse = true })
o.bind("SUPER + mouse:272", "移动窗口", hl.dsp.window.drag(), { mouse = true })
o.bind("SUPER + mouse:273", "调整窗口大小", hl.dsp.window.resize(), { mouse = true })
o.bind("SUPER + G", "Toggle window grouping", hl.dsp.group.toggle())
o.bind("SUPER + ALT + G", "Move active window out of group", hl.dsp.window.move({ out_of_group = true }))
o.bind("SUPER + G", "切换窗口分组", hl.dsp.group.toggle())
o.bind("SUPER + ALT + G", "将窗口移出分组", hl.dsp.window.move({ out_of_group = true }))
o.bind("SUPER + ALT + LEFT", "Move window to group on left", hl.dsp.window.move({ into_group = "l" }))
o.bind("SUPER + ALT + RIGHT", "Move window to group on right", hl.dsp.window.move({ into_group = "r" }))
o.bind("SUPER + ALT + UP", "Move window to group on top", hl.dsp.window.move({ into_group = "u" }))
o.bind("SUPER + ALT + DOWN", "Move window to group on bottom", hl.dsp.window.move({ into_group = "d" }))
o.bind("SUPER + ALT + LEFT", "窗口并入左侧分组", hl.dsp.window.move({ into_group = "l" }))
o.bind("SUPER + ALT + RIGHT", "窗口并入右侧分组", hl.dsp.window.move({ into_group = "r" }))
o.bind("SUPER + ALT + UP", "窗口并入上方分组", hl.dsp.window.move({ into_group = "u" }))
o.bind("SUPER + ALT + DOWN", "窗口并入下方分组", hl.dsp.window.move({ into_group = "d" }))
o.bind("SUPER + ALT + TAB", "Next window in group", hl.dsp.group.next())
o.bind("SUPER + ALT + SHIFT + TAB", "Previous window in group", hl.dsp.group.prev())
o.bind("SUPER + ALT + TAB", "组内下一窗口", hl.dsp.group.next())
o.bind("SUPER + ALT + SHIFT + TAB", "组内上一窗口", hl.dsp.group.prev())
o.bind("SUPER + CTRL + LEFT", "Move grouped window focus left", hl.dsp.group.prev())
o.bind("SUPER + CTRL + RIGHT", "Move grouped window focus right", hl.dsp.group.next())
o.bind("SUPER + CTRL + LEFT", "组内焦点左移", hl.dsp.group.prev())
o.bind("SUPER + CTRL + RIGHT", "组内焦点右移", hl.dsp.group.next())
o.bind("SUPER + ALT + mouse_down", "Next window in group", hl.dsp.group.next())
o.bind("SUPER + ALT + mouse_up", "Previous window in group", hl.dsp.group.prev())
o.bind("SUPER + ALT + mouse_down", "组内下一窗口", hl.dsp.group.next())
o.bind("SUPER + ALT + mouse_up", "组内上一窗口", hl.dsp.group.prev())
for index = 1, 5 do
o.bind("SUPER + ALT + code:" .. tostring(index + 9), "Switch to group window " .. index, hl.dsp.group.active({ index = index }))
o.bind("SUPER + ALT + code:" .. tostring(index + 9), "切换到组内窗口 " .. index, hl.dsp.group.active({ index = index }))
end
o.bind("SUPER + SLASH", "Monitor scaling up", "omarchy-hyprland-monitor-scaling up")
o.bind("SUPER + ALT + SLASH", "Monitor scaling down", "omarchy-hyprland-monitor-scaling down")
o.bind("SUPER + SLASH", "显示器缩放增大", "omarchy-hyprland-monitor-scaling up")
o.bind("SUPER + ALT + SLASH", "显示器缩放减小", "omarchy-hyprland-monitor-scaling down")
+54 -54
View File
@@ -1,45 +1,45 @@
o.bind("SUPER + SPACE", "Omarchy menu", "omarchy-menu toggle")
o.bind("SUPER + ALT + SPACE", "Apps menu", "omarchy-menu toggle apps")
o.bind("SUPER + CTRL + E", "Emojis", "omarchy-shell shell toggle omarchy.emojis")
o.bind("SUPER + CTRL + C", "Capture menu", "omarchy-menu toggle capture")
o.bind("SUPER + CTRL + O", "Toggle menu", "omarchy-menu toggle toggle")
o.bind("SUPER + CTRL + H", "Hardware menu", "omarchy-menu toggle hardware")
o.bind("SUPER + SHIFT + code:201", "Omarchy menu", "omarchy-menu toggle root")
o.bind("SUPER + ESCAPE", "System menu", "omarchy-menu toggle system")
o.bind("XF86PowerOff", "Power menu", "omarchy-menu toggle system", { locked = true })
o.bind("SUPER + K", "Keybindings", "omarchy-menu-keybindings")
o.bind("SUPER + ALT + K", "Tmux keybindings", "omarchy-menu-tmux-keybindings")
o.bind("SUPER + CTRL + K", "Herdr keybindings", "omarchy-menu-herdr-keybindings")
o.bind("SUPER + CTRL + Q", "Calculator", "omacalc")
o.bind("XF86Calculator", "Calculator", "omacalc")
o.bind("SUPER + SPACE", "Omarchy 菜单", "omarchy-menu toggle")
o.bind("SUPER + ALT + SPACE", "应用菜单", "omarchy-menu toggle apps")
o.bind("SUPER + CTRL + E", "表情", "omarchy-shell shell toggle omarchy.emojis")
o.bind("SUPER + CTRL + C", "截取菜单", "omarchy-menu toggle capture")
o.bind("SUPER + CTRL + O", "切换菜单", "omarchy-menu toggle toggle")
o.bind("SUPER + CTRL + H", "硬件菜单", "omarchy-menu toggle hardware")
o.bind("SUPER + SHIFT + code:201", "Omarchy 菜单", "omarchy-menu toggle root")
o.bind("SUPER + ESCAPE", "系统菜单", "omarchy-menu toggle system")
o.bind("XF86PowerOff", "电源菜单", "omarchy-menu toggle system", { locked = true })
o.bind("SUPER + K", "快捷键", "omarchy-menu-keybindings")
o.bind("SUPER + ALT + K", "Tmux 快捷键", "omarchy-menu-tmux-keybindings")
o.bind("SUPER + CTRL + K", "Herdr 快捷键", "omarchy-menu-herdr-keybindings")
o.bind("SUPER + CTRL + Q", "计算器", "omacalc")
o.bind("XF86Calculator", "计算器", "omacalc")
o.bind_toggle("SUPER + SHIFT + SPACE", "Toggle top bar", "bar")
o.bind("SUPER + CTRL + SPACE", "Background switcher", "omarchy-menu toggle background")
o.bind("SUPER + SHIFT + CTRL + SPACE", "Theme menu", "omarchy-menu toggle theme")
o.bind("SUPER + BACKSPACE", "Toggle window transparency", "omarchy-hyprland-window-transparency-toggle")
o.bind("SUPER + SHIFT + BACKSPACE", "Toggle window gaps", "omarchy-hyprland-window-gaps-toggle")
o.bind("SUPER + CTRL + BACKSPACE", "Toggle single-window square aspect", "omarchy-hyprland-window-single-square-aspect-toggle")
o.bind_toggle("SUPER + SHIFT + SPACE", "切换顶栏", "bar")
o.bind("SUPER + CTRL + SPACE", "壁纸切换", "omarchy-menu toggle background")
o.bind("SUPER + SHIFT + CTRL + SPACE", "主题菜单", "omarchy-menu toggle theme")
o.bind("SUPER + BACKSPACE", "切换窗口透明", "omarchy-hyprland-window-transparency-toggle")
o.bind("SUPER + SHIFT + BACKSPACE", "切换窗口间距", "omarchy-hyprland-window-gaps-toggle")
o.bind("SUPER + CTRL + BACKSPACE", "切换单窗口方形比例", "omarchy-hyprland-window-single-square-aspect-toggle")
-- xkbcommon names the comma keysym "comma"; the upper-case "COMMA" does not match.
o.bind("SUPER + comma", "Dismiss last notification", "omarchy-shell notifications dismissOne")
o.bind("SUPER + SHIFT + comma", "Dismiss all notifications", "omarchy-shell notifications dismissAll")
o.bind_toggle("SUPER + CTRL + comma", "Toggle silencing notifications", "notification-silencing")
o.bind("SUPER + ALT + comma", "Invoke last notification", "omarchy-shell notifications invokeLast")
o.bind("SUPER + SHIFT + ALT + comma", "Open notification history", "omarchy-shell notifications showHistory")
o.bind("SUPER + comma", "清除最近通知", "omarchy-shell notifications dismissOne")
o.bind("SUPER + SHIFT + comma", "清除全部通知", "omarchy-shell notifications dismissAll")
o.bind_toggle("SUPER + CTRL + comma", "切换通知免打扰", "notification-silencing")
o.bind("SUPER + ALT + comma", "打开最近通知", "omarchy-shell notifications invokeLast")
o.bind("SUPER + SHIFT + ALT + comma", "通知历史", "omarchy-shell notifications showHistory")
o.bind_toggle("SUPER + CTRL + I", "Toggle locking on idle", "idle")
o.bind_toggle("SUPER + CTRL + N", "Toggle nightlight", "nightlight")
o.bind("SUPER + CTRL + Delete", "Toggle laptop display", "omarchy-hyprland-monitor-internal toggle")
o.bind("SUPER + CTRL + ALT + Delete", "Toggle laptop display mirroring", "omarchy-hyprland-monitor-internal-mirror toggle")
o.bind_toggle("SUPER + CTRL + I", "切换空闲锁定", "idle")
o.bind_toggle("SUPER + CTRL + N", "切换夜间模式", "nightlight")
o.bind("SUPER + CTRL + Delete", "切换笔记本屏幕", "omarchy-hyprland-monitor-internal toggle")
o.bind("SUPER + CTRL + ALT + Delete", "切换笔记本镜像显示", "omarchy-hyprland-monitor-internal-mirror toggle")
o.bind("switch:on:Lid Switch", nil, "omarchy-system-lid-close", { locked = true })
o.bind("switch:off:Lid Switch", nil, "omarchy-hyprland-monitor-clamshell", { locked = true })
o.bind("PRINT", "Screenshot", "omarchy-capture-screenshot")
o.bind("ALT + PRINT", "Screenrecording", "omarchy-capture-screenrecording --stop-recording || omarchy-menu toggle trigger.capture.screenrecord")
o.bind("SUPER + ALT + code:34", "Make webcam overlay smaller", "omarchy-capture-webcam-resize smaller")
o.bind("SUPER + ALT + code:35", "Make webcam overlay larger", "omarchy-capture-webcam-resize larger")
o.bind("SUPER + PRINT", "Color picker", "pkill hyprpicker || hyprpicker -a")
o.bind("SUPER + CTRL + PRINT", "Extract text (OCR) from screenshot", "omarchy-capture-text")
o.bind("PRINT", "截图", "omarchy-capture-screenshot")
o.bind("ALT + PRINT", "录屏", "omarchy-capture-screenrecording --stop-recording || omarchy-menu toggle trigger.capture.screenrecord")
o.bind("SUPER + ALT + code:34", "缩小摄像头悬浮窗", "omarchy-capture-webcam-resize smaller")
o.bind("SUPER + ALT + code:35", "放大摄像头悬浮窗", "omarchy-capture-webcam-resize larger")
o.bind("SUPER + PRINT", "取色器", "pkill hyprpicker || hyprpicker -a")
o.bind("SUPER + CTRL + PRINT", "截图取字(OCR", "omarchy-capture-text")
-- Keyboard control for the slurp region picker (see omarchy-capture-region).
-- The binds live exactly as long as a selection layer is on screen (slurp
@@ -81,26 +81,26 @@ hl.on("layer.closed", function(layer)
end
end)
o.bind("SUPER + CTRL + S", "Share", "omarchy-menu toggle share")
o.bind("SUPER + CTRL + S", "分享", "omarchy-menu toggle share")
o.bind("SUPER + CTRL + PERIOD", "Transcode", "omarchy-transcode")
o.bind("SUPER + CTRL + PERIOD", "转码", "omarchy-transcode")
o.bind("SUPER + CTRL + R", "Set reminder", "omarchy-menu toggle reminder-set")
o.bind("SUPER + CTRL + ALT + R", "Show reminders", "omarchy-reminder show")
o.bind("SUPER + SHIFT + CTRL + R", "Clear reminders", "omarchy-reminder clear")
o.bind("SUPER + CTRL + R", "设置提醒", "omarchy-menu toggle reminder-set")
o.bind("SUPER + CTRL + ALT + R", "查看提醒", "omarchy-reminder show")
o.bind("SUPER + SHIFT + CTRL + R", "清除提醒", "omarchy-reminder clear")
o.bind("SUPER + CTRL + ALT + T", "Show time", "omarchy-notification-time")
o.bind("SUPER + CTRL + ALT + B", "Show battery remaining", "omarchy-notification-battery")
o.bind("SUPER + CTRL + ALT + W", "Toggle weather", "omarchy-notification-weather")
o.bind("SUPER + CTRL + ALT + T", "查看时间", "omarchy-notification-time")
o.bind("SUPER + CTRL + ALT + B", "查看剩余电量", "omarchy-notification-battery")
o.bind("SUPER + CTRL + ALT + W", "切换天气", "omarchy-notification-weather")
o.bind("SUPER + SHIFT + CTRL + A", "Agent", "omarchy-agent --pick")
o.bind("SUPER + CTRL + A", "Audio", "omarchy-shell shell toggle omarchy.audio")
o.bind("SUPER + CTRL + B", "Bluetooth", "omarchy-shell shell toggle omarchy.bluetooth")
o.bind("SUPER + CTRL + D", "Display", "omarchy-shell shell toggle omarchy.monitor")
o.bind("SUPER + CTRL + ALT + D", "Calendar", "omarchy-shell shell toggle omarchy.clock")
o.bind("SUPER + CTRL + W", "Network", "omarchy-shell shell toggle omarchy.network")
o.bind("SUPER + CTRL + P", "Power", "omarchy-shell shell toggle omarchy.power")
o.bind("SUPER + CTRL + T", "Activity", { tui = "btop" })
o.bind("SUPER + CTRL + A", "音频", "omarchy-shell shell toggle omarchy.audio")
o.bind("SUPER + CTRL + B", "蓝牙", "omarchy-shell shell toggle omarchy.bluetooth")
o.bind("SUPER + CTRL + D", "显示", "omarchy-shell shell toggle omarchy.monitor")
o.bind("SUPER + CTRL + ALT + D", "日历", "omarchy-shell shell toggle omarchy.clock")
o.bind("SUPER + CTRL + W", "网络", "omarchy-shell shell toggle omarchy.network")
o.bind("SUPER + CTRL + P", "电源", "omarchy-shell shell toggle omarchy.power")
o.bind("SUPER + CTRL + T", "系统活动", { tui = "btop" })
-- The letters above name a panel; the numbers count them. 1 is the leftmost
-- panel in the bar's right section, and a widget with no panel of its own (the
@@ -109,18 +109,18 @@ o.bind("SUPER + CTRL + T", "Activity", { tui = "btop" })
for panel = 1, 9 do
o.bind(
"SUPER + CTRL + code:" .. tostring(panel + 9),
"Bar panel " .. panel,
"顶栏面板 " .. panel,
"omarchy-shell -q shell togglePanelAt right " .. panel
)
end
o.bind("SUPER + CTRL + Z", "Zoom in", function()
o.bind("SUPER + CTRL + Z", "放大", function()
local zoom = hl.get_config("cursor.zoom_factor") or 1
hl.config({ cursor = { zoom_factor = zoom + 1 } })
end)
o.bind("SUPER + CTRL + ALT + Z", "Reset zoom", function()
o.bind("SUPER + CTRL + ALT + Z", "重置缩放", function()
hl.config({ cursor = { zoom_factor = 1 } })
end)
o.bind("SUPER + CTRL + L", "Lock system", "omarchy-system-lock")
o.bind("SUPER + CTRL + L", "锁定系统", "omarchy-system-lock")
+3 -3
View File
@@ -1,5 +1,5 @@
if o.cmd_present("voxtype") then
o.bind("SUPER + CTRL + X", "Toggle dictation", "voxtype record toggle")
o.bind("F9", "Start dictation (push-to-talk)", "voxtype record start")
o.bind("F9", "Stop dictation (push-to-talk)", "voxtype record stop", { release = true })
o.bind("SUPER + CTRL + X", "切换语音听写", "voxtype record toggle")
o.bind("F9", "开始听写(按住说话)", "voxtype record start")
o.bind("F9", "停止听写(按住说话)", "voxtype record stop", { release = true })
end
-5
View File
@@ -19,11 +19,6 @@ kbd-layout-viewer5
kcm_fcitx5
kcm_kaccounts
kvantummanager
libreoffice-base
libreoffice-draw
libreoffice-math
libreoffice-startcenter
libreoffice-xsltfilter
limine-snapper-restore
lstopo
org.fcitx.Fcitx5
+200 -177
View File
@@ -21,131 +21,135 @@
// installed instead of vanishing from the list.
// Root Menu
"apps": {"icon":"󰀻","label":"Apps","aliases":["app","applications"],"provider":"apps"},
"learn": {"icon":"󰧑","label":"Learn"},
"trigger": {"icon":"󱓞","label":"Trigger"},
"style": {"icon":"","label":"Style"},
"setup": {"icon":"","label":"Setup","aliases":["settings"]},
"install": {"icon":"󰉉","label":"Install"},
"remove": {"icon":"󰭌","label":"Remove","aliases":["uninstall"]},
"update": {"icon":"","label":"Update","aliases":["restart","refresh"]},
"about": {"icon":"","label":"About","action":"omarchy-launch-about"},
"system": {"icon":"","label":"System","aliases":["power-menu"]},
"apps": {"icon":"󰀻","label":"应用","aliases":["app","applications"],"provider":"apps"},
"learn": {"icon":"󰧑","label":"学习"},
"trigger": {"icon":"󱓞","label":"触发"},
"style": {"icon":"","label":"风格"},
"setup": {"icon":"","label":"设置","aliases":["settings"]},
"install": {"icon":"󰉉","label":"安装"},
"remove": {"icon":"󰭌","label":"移除","aliases":["uninstall"]},
"update": {"icon":"","label":"更新","aliases":["restart","refresh"]},
"about": {"icon":"","label":"关于","action":"omarchy-launch-about"},
"system": {"icon":"","label":"系统","aliases":["power-menu"]},
// System
"system.screensaver": {"icon":"󱄄","label":"Screensaver","action":"omarchy-launch-screensaver force"},
"system.lock": {"icon":"","label":"Lock","action":"omarchy-system-lock"},
"system.suspend": {"icon":"󰒲","label":"Suspend","when":"! omarchy-toggle-enabled suspend-off","action":"systemctl suspend"},
"system.hibernate": {"icon":"󰤁","label":"Hibernate","when":"omarchy-hibernation-available","action":"systemctl hibernate"},
"system.logout": {"icon":"󰍃","label":"Logout","action":"omarchy-system-logout"},
"system.reboot": {"icon":"󰜉","label":"Reboot","action":"omarchy-system-reboot"},
"system.shutdown": {"icon":"󰐥","label":"Shutdown","action":"omarchy-system-shutdown"},
"system.screensaver": {"icon":"󱄄","label":"屏保","action":"omarchy-launch-screensaver force"},
"system.lock": {"icon":"","label":"锁屏","action":"omarchy-system-lock"},
"system.suspend": {"icon":"󰒲","label":"睡眠","when":"! omarchy-toggle-enabled suspend-off","action":"systemctl suspend"},
"system.hibernate": {"icon":"󰤁","label":"休眠","when":"omarchy-hibernation-available","action":"systemctl hibernate"},
"system.logout": {"icon":"󰍃","label":"注销","action":"omarchy-system-logout"},
"system.reboot": {"icon":"󰜉","label":"重启","action":"omarchy-system-reboot"},
"system.shutdown": {"icon":"󰐥","label":"关机","action":"omarchy-system-shutdown"},
// Learn
"learn.keybindings": {"icon":"","label":"Keybindings","action":"omarchy-menu-keybindings"},
"learn.omarchy": {"icon":"","label":"Omarchy","action":"omarchy-launch-webapp 'https://omarchy.org/manual/'"},
"learn.keybindings": {"icon":"","label":"快捷键","action":"omarchy-menu-keybindings"},
"learn.omarchy": {"icon":"","label":"Omarchy 手册","action":"omarchy-launch-webapp 'https://omarchy.org/manual/'"},
"learn.hyprland": {"icon":"","label":"Hyprland","action":"omarchy-launch-webapp 'https://wiki.hypr.land/'"},
"learn.arch": {"icon":"󰣇","label":"Arch","action":"omarchy-launch-webapp 'https://wiki.archlinux.org/title/Main_page'"},
"learn.neovim": {"icon":"","label":"Neovim","action":"omarchy-launch-webapp 'https://www.lazyvim.org/keymaps'"},
"learn.bash": {"icon":"󱆃","label":"Bash","action":"omarchy-launch-webapp 'https://devhints.io/bash'"},
"learn.tmux-keybindings": {"icon":"","label":"Tmux","action":"omarchy-menu-tmux-keybindings"},
"learn.herdr-keybindings": {"icon":"","label":"Herdr","action":"omarchy-menu-herdr-keybindings"},
"learn.community": {"icon":"󰙯","label":"Community","action":"omarchy-launch-discord-community"},
"learn.community": {"icon":"󰙯","label":"社区","action":"omarchy-launch-discord-community"},
// Trigger
"trigger.emoji": {"icon":"","label":"Emoji","aliases":["emoji","emojis"],"action":"omarchy-menu-emoji"},
"trigger.reminder": {"icon":"󰢌","label":"Reminder","aliases":["reminder"]},
"trigger.capture": {"icon":"","label":"Capture","aliases":["capture","screenshot","screenrecord","screen-record","screenrecording"]},
"trigger.capture.screenshot": {"icon":"","label":"Screenshot","action":"omarchy-capture-screenshot"},
"trigger.capture.screenrecord.stop": {"icon":"","label":"Stop Screenrecording","when":"pgrep -f '^gpu-screen-recorder'","action":"omarchy-capture-screenrecording --stop-recording"},
"trigger.capture.screenrecord": {"icon":"","label":"Screenrecord"},
"trigger.capture.text": {"icon":"󰴑","label":"Text","action":"omarchy-capture-text"},
"trigger.capture.qr": {"icon":"󰐲","label":"QR Code","action":"omarchy-capture-qr"},
"trigger.capture.color": {"icon":"󰃉","label":"Color","action":"pkill hyprpicker || hyprpicker -a"},
"trigger.capture.screenrecord.no-audio": {"icon":"","label":"With no audio","action":"omarchy-capture-screenrecording"},
"trigger.capture.screenrecord.desktop-audio": {"icon":"","label":"With desktop audio","action":"omarchy-capture-screenrecording --with-desktop-audio"},
"trigger.capture.screenrecord.microphone": {"icon":"","label":"With desktop + microphone audio","action":"omarchy-capture-screenrecording --with-desktop-audio --with-microphone-audio"},
"trigger.capture.screenrecord.webcam": {"icon":"","label":"With desktop + microphone audio + webcam","when":"omarchy-hw-webcam","action":"omarchy-capture-screenrecording-with-webcam"},
"trigger.transcode": {"icon":"󰧸","label":"Transcode","action":"omarchy-transcode"},
"trigger.share": {"icon":"","label":"Share","aliases":["share"]},
"trigger.toggle": {"icon":"󰔎","label":"Toggle","aliases":["toggle","toggles"]},
"trigger.hardware": {"icon":"","label":"Hardware","aliases":["hardware","hw"]},
"trigger.tests": {"icon":"󰓅","label":"Speed Test"},
"trigger.hardware.laptop-display": {"icon":"󰛧","label":"Laptop Display","when":"omarchy-hw-laptop","action":"omarchy-hyprland-monitor-internal toggle"},
"trigger.hardware.mirror-display": {"icon":"󰍹","label":"Mirror Display","when":"omarchy-hw-laptop","action":"omarchy-hyprland-monitor-internal-mirror toggle"},
"trigger.hardware.hybrid-gpu": {"icon":"","label":"Hybrid GPU","when":"omarchy-hw-hybrid-gpu","action":"omarchy-launch-floating-terminal-with-presentation omarchy-toggle-hybrid-gpu"},
"trigger.hardware.touchpad": {"icon":"󰟸","label":"Touchpad","when":"omarchy-hw-touchpad","action":"omarchy-toggle-touchpad"},
"trigger.hardware.touchpad-haptics": {"icon":"󰌌","label":"Touchpad Haptics"},
"trigger.hardware.touchpad-haptics.low": {"icon":"󰌌","label":"low","when":"omarchy-hw-dell-xps-haptic-touchpad && omarchy-cmd-present dell-xps-touchpad-haptics","checked":"[[ \"$(dell-xps-touchpad-haptics get)\" == \"low\" ]]","action":"dell-xps-touchpad-haptics set low"},
"trigger.hardware.touchpad-haptics.mid": {"icon":"󰌌","label":"mid","when":"omarchy-hw-dell-xps-haptic-touchpad && 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 && 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","when":"omarchy-hw-touchscreen","action":"omarchy-toggle-touchscreen"},
"trigger.reminder.set": {"icon":"󰢌","label":"Set one","aliases":["reminder-set","remind"],"action":"omarchy-reminder -i"},
"trigger.reminder.show": {"icon":"󰢌","label":"Show all","action":"omarchy-reminder show"},
"trigger.reminder.clear": {"icon":"󰢌","label":"Clear all","action":"omarchy-reminder clear"},
"trigger.share.clipboard": {"icon":"","label":"Clipboard","action":"omarchy-menu-share clipboard"},
"trigger.share.file": {"icon":"","label":"File","action":"omarchy-menu-share file"},
"trigger.share.folder": {"icon":"","label":"Folder","action":"omarchy-menu-share folder"},
"trigger.share.receive": {"icon":"󰥦","label":"Receive","action":"uwsm-app -- localsend"},
"trigger.toggle.idle-lock": {"icon":"󰅶","label":"Stay Awake","action":"omarchy-toggle-idle"},
"trigger.toggle.notifications": {"icon":"󰂛","label":"Notifications","action":"omarchy-toggle-notification-silencing"},
"trigger.toggle.crash-capture": {"icon":"󱚡","label":"Crash Capture","action":"omarchy-toggle-crash-capture"},
"trigger.toggle.screensaver": {"icon":"󱄄","label":"Screensaver","action":"omarchy-toggle-screensaver"},
"trigger.toggle.nightlight": {"icon":"󰔎","label":"Nightlight","action":"omarchy-toggle-nightlight"},
"trigger.toggle.top-bar": {"icon":"󰍜","label":"Menu Bar","action":"omarchy-toggle-bar"},
"trigger.toggle.battery-percentage": {"icon":"󰁹","label":"Battery Percentage","when":"omarchy-hw-laptop","action":"omarchy-shell omarchy.power togglePercentage"},
"trigger.toggle.workspace-layout": {"icon":"󱂬","label":"Workspace Layout","action":"omarchy-hyprland-workspace-layout-toggle"},
"trigger.toggle.window-gaps": {"icon":"","label":"Window Gaps","action":"omarchy-hyprland-window-gaps-toggle"},
"trigger.toggle.one-window-ratio": {"icon":"","label":"1-Window Ratio","action":"omarchy-hyprland-window-single-square-aspect-toggle"},
"trigger.tests.network-speedtest": {"icon":"󰓅","label":"Network Speed Test","action":"omarchy-shell shell summon omarchy.speedtest"},
"trigger.tests.disk-speedtest": {"icon":"󰋊","label":"Disk Speed Test","action":"omarchy-shell shell summon omarchy.disk-speedtest"},
"trigger.emoji": {"icon":"","label":"表情","aliases":["emoji","emojis"],"action":"omarchy-menu-emoji"},
"trigger.reminder": {"icon":"󰢌","label":"提醒","aliases":["reminder"]},
"trigger.capture": {"icon":"","label":"截取","aliases":["capture","screenshot","screenrecord","screen-record","screenrecording"]},
"trigger.capture.screenshot": {"icon":"","label":"截图","action":"omarchy-capture-screenshot"},
"trigger.capture.screenrecord.stop": {"icon":"","label":"停止录屏","when":"pgrep -f '^gpu-screen-recorder'","action":"omarchy-capture-screenrecording --stop-recording"},
"trigger.capture.screenrecord": {"icon":"","label":"录屏"},
"trigger.capture.text": {"icon":"󰴑","label":"取字 OCR","action":"omarchy-capture-text"},
"trigger.capture.qr": {"icon":"󰐲","label":"二维码","action":"omarchy-capture-qr"},
"trigger.capture.color": {"icon":"󰃉","label":"取色","action":"pkill hyprpicker || hyprpicker -a"},
"trigger.capture.screenrecord.no-audio": {"icon":"","label":"无音频","action":"omarchy-capture-screenrecording"},
"trigger.capture.screenrecord.desktop-audio": {"icon":"","label":"含桌面音频","action":"omarchy-capture-screenrecording --with-desktop-audio"},
"trigger.capture.screenrecord.microphone": {"icon":"","label":"含桌面与麦克风音频","action":"omarchy-capture-screenrecording --with-desktop-audio --with-microphone-audio"},
"trigger.capture.screenrecord.webcam": {"icon":"","label":"含桌面、麦克风与摄像头","when":"omarchy-hw-webcam","action":"omarchy-capture-screenrecording-with-webcam"},
"trigger.transcode": {"icon":"󰧸","label":"转码","action":"omarchy-transcode"},
"trigger.share": {"icon":"","label":"分享","aliases":["share"]},
"trigger.toggle": {"icon":"󰔎","label":"开关","aliases":["toggle","toggles"]},
"trigger.hardware": {"icon":"","label":"硬件","aliases":["hardware","hw"]},
"trigger.tests": {"icon":"󰓅","label":"测速"},
"trigger.hardware.laptop-display": {"icon":"󰛧","label":"笔记本屏幕","when":"omarchy-hw-laptop","action":"omarchy-hyprland-monitor-internal toggle"},
"trigger.hardware.mirror-display": {"icon":"󰍹","label":"镜像显示","when":"omarchy-hw-laptop","action":"omarchy-hyprland-monitor-internal-mirror toggle"},
"trigger.hardware.hybrid-gpu": {"icon":"","label":"混合显卡","when":"omarchy-hw-hybrid-gpu","action":"omarchy-launch-floating-terminal-with-presentation omarchy-toggle-hybrid-gpu"},
"trigger.hardware.touchpad": {"icon":"󰟸","label":"触控板","when":"omarchy-hw-touchpad","action":"omarchy-toggle-touchpad"},
"trigger.hardware.touchpad-haptics": {"icon":"󰌌","label":"触控板振动"},
"trigger.hardware.touchpad-haptics.low": {"icon":"󰌌","label":"","when":"omarchy-hw-dell-xps-haptic-touchpad && omarchy-cmd-present dell-xps-touchpad-haptics","checked":"[[ \"$(dell-xps-touchpad-haptics get)\" == \"low\" ]]","action":"dell-xps-touchpad-haptics set low"},
"trigger.hardware.touchpad-haptics.mid": {"icon":"󰌌","label":"","when":"omarchy-hw-dell-xps-haptic-touchpad && 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":"","when":"omarchy-hw-dell-xps-haptic-touchpad && 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":"触摸屏","when":"omarchy-hw-touchscreen","action":"omarchy-toggle-touchscreen"},
"trigger.reminder.set": {"icon":"󰢌","label":"设一个提醒","aliases":["reminder-set","remind"],"action":"omarchy-reminder -i"},
"trigger.reminder.show": {"icon":"󰢌","label":"查看全部","action":"omarchy-reminder show"},
"trigger.reminder.clear": {"icon":"󰢌","label":"清除全部","action":"omarchy-reminder clear"},
"trigger.share.clipboard": {"icon":"","label":"剪贴板","action":"omarchy-menu-share clipboard"},
"trigger.share.file": {"icon":"","label":"文件","action":"omarchy-menu-share file"},
"trigger.share.folder": {"icon":"","label":"文件夹","action":"omarchy-menu-share folder"},
"trigger.share.receive": {"icon":"󰥦","label":"接收","action":"uwsm-app -- localsend"},
"trigger.toggle.idle-lock": {"icon":"󰅶","label":"保持唤醒","action":"omarchy-toggle-idle"},
"trigger.toggle.notifications": {"icon":"󰂛","label":"通知","action":"omarchy-toggle-notification-silencing"},
"trigger.toggle.crash-capture": {"icon":"󱚡","label":"崩溃捕获","action":"omarchy-toggle-crash-capture"},
"trigger.toggle.screensaver": {"icon":"󱄄","label":"屏保","action":"omarchy-toggle-screensaver"},
"trigger.toggle.nightlight": {"icon":"󰔎","label":"夜间模式","action":"omarchy-toggle-nightlight"},
"trigger.toggle.top-bar": {"icon":"󰍜","label":"菜单栏","action":"omarchy-toggle-bar"},
"trigger.toggle.battery-percentage": {"icon":"󰁹","label":"电量百分比","when":"omarchy-hw-laptop","action":"omarchy-shell omarchy.power togglePercentage"},
"trigger.toggle.workspace-layout": {"icon":"󱂬","label":"工作区布局","action":"omarchy-hyprland-workspace-layout-toggle"},
"trigger.toggle.window-gaps": {"icon":"","label":"窗口间距","action":"omarchy-hyprland-window-gaps-toggle"},
"trigger.toggle.one-window-ratio": {"icon":"","label":"单窗口比例","action":"omarchy-hyprland-window-single-square-aspect-toggle"},
"trigger.tests.network-speedtest": {"icon":"󰓅","label":"网络测速","action":"omarchy-shell shell summon omarchy.speedtest"},
"trigger.tests.disk-speedtest": {"icon":"󰋊","label":"磁盘测速","action":"omarchy-shell shell summon omarchy.disk-speedtest"},
// Style
"style.theme": {"icon":"󰸌","label":"Theme","aliases":["theme","themes"],"action":"theme=$(omarchy-theme-switcher); [[ -n $theme ]] && omarchy-theme-set \"$theme\""},
"style.background": {"icon":"","label":"Background","aliases":["background","wallpaper"],"action":"background=$(omarchy-theme-bg-switcher); [[ -n $background ]] && omarchy-theme-bg-set \"$background\""},
"style.unlock": {"icon":"󰟵","label":"Unlock","aliases":["unlock"],"action":"unlock=$(omarchy-plymouth-switcher); if [[ $unlock == default ]]; then omarchy-launch-floating-terminal-with-presentation omarchy-plymouth-reset; elif [[ -n $unlock ]]; then omarchy-launch-floating-terminal-with-presentation \"omarchy-plymouth-set-by-theme '$unlock'\"; fi"},
"style.font": {"icon":"","label":"Font","provider":"fonts"},
"style.bar": {"icon":"󰍜","label":"Menu Bar"},
"style.bar.position": {"icon":"","label":"Position"},
"style.bar.transparency": {"icon":"󰂵","label":"Transparency","action":"omarchy-bar transparent toggle"},
"style.theme": {"icon":"󰸌","label":"主题","aliases":["theme","themes"],"action":"theme=$(omarchy-theme-switcher); [[ -n $theme ]] && omarchy-theme-set \"$theme\""},
"style.background": {"icon":"","label":"壁纸","aliases":["background","wallpaper"],"action":"background=$(omarchy-theme-bg-switcher); [[ -n $background ]] && omarchy-theme-bg-set \"$background\""},
"style.unlock": {"icon":"󰟵","label":"解锁画面","aliases":["unlock"],"action":"unlock=$(omarchy-plymouth-switcher); if [[ $unlock == default ]]; then omarchy-launch-floating-terminal-with-presentation omarchy-plymouth-reset; elif [[ -n $unlock ]]; then omarchy-launch-floating-terminal-with-presentation \"omarchy-plymouth-set-by-theme '$unlock'\"; fi"},
"style.font": {"icon":"","label":"字体","provider":"fonts"},
"style.bar": {"icon":"󰍜","label":"菜单栏"},
"style.bar.position": {"icon":"","label":"位置"},
"style.bar.transparency": {"icon":"󰂵","label":"透明","action":"omarchy-bar transparent toggle"},
"style.hyprland": {"icon":"","label":"Hyprland","action":"omarchy-launch-config-editor \"$HOME/.config/hypr/looknfeel.lua\""},
"style.screensaver": {"icon":"󱄄","label":"Screensaver"},
"style.about": {"icon":"","label":"About"},
"style.bar.position.top": {"icon":"󰁝","label":"Top","action":"omarchy-bar position top"},
"style.bar.position.bottom": {"icon":"󰁅","label":"Bottom","action":"omarchy-bar position bottom"},
"style.bar.position.left": {"icon":"󰁍","label":"Left","action":"omarchy-bar position left"},
"style.bar.position.right": {"icon":"󰁔","label":"Right","action":"omarchy-bar position right"},
"style.about.text": {"icon":"","label":"Edit Text","action":"omarchy-branding-about text"},
"style.about.image": {"icon":"","label":"Set From Image","action":"omarchy-branding-about image"},
"style.about.default": {"icon":"","label":"Restore Default","action":"omarchy-branding-about reset"},
"style.screensaver.text": {"icon":"","label":"Edit Text","action":"omarchy-branding-screensaver text"},
"style.screensaver.image": {"icon":"","label":"Set From Image","action":"omarchy-branding-screensaver image"},
"style.screensaver.default": {"icon":"","label":"Restore Default","action":"omarchy-branding-screensaver reset"},
"style.screensaver": {"icon":"󱄄","label":"屏保"},
"style.about": {"icon":"","label":"关于页"},
"style.bar.position.top": {"icon":"󰁝","label":"顶部","action":"omarchy-bar position top"},
"style.bar.position.bottom": {"icon":"󰁅","label":"底部","action":"omarchy-bar position bottom"},
"style.bar.position.left": {"icon":"󰁍","label":"左侧","action":"omarchy-bar position left"},
"style.bar.position.right": {"icon":"󰁔","label":"右侧","action":"omarchy-bar position right"},
"style.about.text": {"icon":"","label":"编辑文字","action":"omarchy-branding-about text"},
"style.about.image": {"icon":"","label":"取自图片","action":"omarchy-branding-about image"},
"style.about.default": {"icon":"","label":"恢复默认","action":"omarchy-branding-about reset"},
"style.screensaver.text": {"icon":"","label":"编辑文字","action":"omarchy-branding-screensaver text"},
"style.screensaver.image": {"icon":"","label":"取自图片","action":"omarchy-branding-screensaver image"},
"style.screensaver.default": {"icon":"","label":"恢复默认","action":"omarchy-branding-screensaver reset"},
// Setup
"setup.monitors": {"icon":"󰍹","label":"Monitors","action":"omarchy-launch-config-editor \"$HOME/.config/hypr/monitors.lua\""},
"setup.keybindings": {"icon":"","label":"Keybindings","when":"[[ -f ~/.config/hypr/bindings.lua ]]","action":"omarchy-launch-config-editor \"$HOME/.config/hypr/bindings.lua\""},
"setup.input": {"icon":"","label":"Input","when":"[[ -f ~/.config/hypr/input.lua ]]","action":"omarchy-launch-config-editor \"$HOME/.config/hypr/input.lua\""},
"setup.network": {"icon":"󰛳","label":"Network","aliases":["network"]},
"setup.monitors": {"icon":"󰍹","label":"显示器","action":"omarchy-launch-config-editor \"$HOME/.config/hypr/monitors.lua\""},
"setup.keybindings": {"icon":"","label":"快捷键","when":"[[ -f ~/.config/hypr/bindings.lua ]]","action":"omarchy-launch-config-editor \"$HOME/.config/hypr/bindings.lua\""},
"setup.input": {"icon":"","label":"输入设备","when":"[[ -f ~/.config/hypr/input.lua ]]","action":"omarchy-launch-config-editor \"$HOME/.config/hypr/input.lua\""},
"setup.network": {"icon":"󰛳","label":"网络","aliases":["network"]},
"setup.network.dns": {"icon":"󰇖","label":"DNS","aliases":["dns"]},
"setup.network.dns.dhcp": {"icon":"󰩟","label":"DHCP","checked":"[[ \"$(omarchy-dns)\" == \"DHCP\" ]]","action":"omarchy-dns DHCP"},
"setup.network.dns.cloudflare": {"icon":"󰅟","label":"Cloudflare","checked":"[[ \"$(omarchy-dns)\" == \"Cloudflare\" ]]","action":"omarchy-dns Cloudflare"},
"setup.network.dns.google": {"icon":"󰊭","label":"Google","checked":"[[ \"$(omarchy-dns)\" == \"Google\" ]]","action":"omarchy-dns Google"},
"setup.network.dns.custom": {"icon":"","label":"Custom","checked":"[[ \"$(omarchy-dns)\" == \"Custom\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-dns Custom'"},
"setup.network.qr": {"icon":"󰐲","label":"QR Code","aliases":["wifi-qr"],"when":"[[ $(omarchy-network-status) == wifi* ]]","action":"omarchy-shell shell summon omarchy.wifiqr"},
"setup.default": {"icon":"","label":"Defaults","aliases":["default","defaults"]},
"setup.default.agent": {"icon":"󰚩","label":"Agent","title":"Default Agent"},
"setup.network.dns.custom": {"icon":"","label":"自定义","checked":"[[ \"$(omarchy-dns)\" == \"Custom\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-dns Custom'"},
"setup.network.qr": {"icon":"󰐲","label":"二维码","aliases":["wifi-qr"],"when":"[[ $(omarchy-network-status) == wifi* ]]","action":"omarchy-shell shell summon omarchy.wifiqr"},
"setup.default": {"icon":"","label":"默认程序","aliases":["default","defaults"]},
"setup.default.agent": {"icon":"󰚩","label":"Agent","title":"默认 Agent"},
"setup.default.agent.agy": {"icon":"󰫢","label":"Antigravity","checked":"[[ \"$(omarchy-default-agent)\" == \"agy\" ]]","action":"omarchy-default-agent agy"},
"setup.default.agent.claude": {"icon":"󰛄","label":"Claude","checked":"[[ \"$(omarchy-default-agent)\" == \"claude\" ]]","action":"omarchy-default-agent claude"},
"setup.default.agent.codex": {"icon":"","iconFont":"omarchy","label":"Codex","checked":"[[ \"$(omarchy-default-agent)\" == \"codex\" ]]","action":"omarchy-default-agent codex"},
"setup.default.agent.copilot": {"icon":"","label":"Copilot","checked":"[[ \"$(omarchy-default-agent)\" == \"copilot\" ]]","action":"omarchy-default-agent copilot"},
"setup.default.agent.crush": {"icon":"󰋑","label":"Crush","checked":"[[ \"$(omarchy-default-agent)\" == \"crush\" ]]","action":"omarchy-default-agent crush"},
"setup.default.agent.deepcode": {"icon":"󱚤","label":"Deep Code","checked":"[[ \"$(omarchy-default-agent)\" == \"deepcode\" ]]","action":"omarchy-default-agent deepcode"},
"setup.default.agent.dim": {"icon":"󱚤","label":"Dim Agent","checked":"[[ \"$(omarchy-default-agent)\" == \"dim\" ]]","action":"omarchy-default-agent dim"},
"setup.default.agent.grok": {"icon":"","iconFont":"omarchy","label":"Grok","checked":"[[ \"$(omarchy-default-agent)\" == \"grok\" ]]","action":"omarchy-default-agent grok"},
"setup.default.agent.kimi": {"icon":"󱚤","label":"Kimi Code","checked":"[[ \"$(omarchy-default-agent)\" == \"kimi\" ]]","action":"omarchy-default-agent kimi"},
"setup.default.agent.omp": {"icon":"","iconFont":"omarchy","label":"omp","checked":"[[ \"$(omarchy-default-agent)\" == \"omp\" ]]","action":"omarchy-default-agent omp"},
"setup.default.agent.opencode": {"icon":"","iconFont":"omarchy","label":"OpenCode","checked":"[[ \"$(omarchy-default-agent)\" == \"opencode\" ]]","action":"omarchy-default-agent opencode"},
"setup.default.agent.ori": {"icon":"","iconFont":"omarchy","label":"Ori","checked":"[[ \"$(omarchy-default-agent)\" == \"ori\" ]]","action":"omarchy-default-agent ori"},
"setup.default.agent.pi": {"icon":"","iconFont":"omarchy","label":"Pi","checked":"[[ \"$(omarchy-default-agent)\" == \"pi\" ]]","action":"omarchy-default-agent pi"},
"setup.default.browser": {"icon":"","label":"Browser","title":"Default Browser"},
"setup.default.agent.cn-hub": {"icon":"󱚤","label":"国产模型组合 (AI Hub)","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-ai-setup"},
"setup.default.browser": {"icon":"","label":"浏览器","title":"默认浏览器"},
"setup.default.browser.chromium": {"icon":"","label":"Chromium","checked":"[[ \"$(omarchy-default-browser)\" == \"chromium\" ]]","action":"omarchy-default-browser chromium"},
"setup.default.browser.chrome": {"icon":"󰊯","label":"Chrome","checked":"[[ \"$(omarchy-default-browser)\" == \"chrome\" ]]","action":"omarchy-default-browser chrome"},
"setup.default.browser.brave": {"icon":"󰖟","label":"Brave","checked":"[[ \"$(omarchy-default-browser)\" == \"brave\" ]]","action":"omarchy-default-browser brave"},
@@ -153,12 +157,12 @@
"setup.default.browser.edge": {"icon":"󰇩","label":"Edge","checked":"[[ \"$(omarchy-default-browser)\" == \"edge\" ]]","action":"omarchy-default-browser edge"},
"setup.default.browser.firefox": {"icon":"","label":"Firefox","checked":"[[ \"$(omarchy-default-browser)\" == \"firefox\" ]]","action":"omarchy-default-browser firefox"},
"setup.default.browser.zen": {"icon":"󰖟","label":"Zen","checked":"[[ \"$(omarchy-default-browser)\" == \"zen\" ]]","action":"omarchy-default-browser zen"},
"setup.default.terminal": {"icon":"","label":"Terminal","title":"Default Terminal"},
"setup.default.terminal": {"icon":"","label":"终端","title":"默认终端"},
"setup.default.terminal.alacritty": {"icon":"","label":"Alacritty","checked":"[[ \"$(omarchy-default-terminal)\" == \"alacritty\" ]]","action":"omarchy-default-terminal alacritty"},
"setup.default.terminal.foot": {"icon":"","label":"Foot","checked":"[[ \"$(omarchy-default-terminal)\" == \"foot\" ]]","action":"omarchy-default-terminal foot"},
"setup.default.terminal.ghostty": {"icon":"","label":"Ghostty","checked":"[[ \"$(omarchy-default-terminal)\" == \"ghostty\" ]]","action":"omarchy-default-terminal ghostty"},
"setup.default.terminal.kitty": {"icon":"","label":"Kitty","checked":"[[ \"$(omarchy-default-terminal)\" == \"kitty\" ]]","action":"omarchy-default-terminal kitty"},
"setup.default.editor": {"icon":"","label":"Editor","title":"Default Editor"},
"setup.default.editor": {"icon":"","label":"编辑器","title":"默认编辑器"},
"setup.default.editor.neovim": {"icon":"","label":"Neovim","checked":"[[ \"$(omarchy-default-editor)\" == \"nvim\" ]]","action":"omarchy-default-editor nvim"},
"setup.default.editor.vscode": {"icon":"","label":"VSCode","checked":"[[ \"$(omarchy-default-editor)\" == \"code\" ]]","action":"omarchy-default-editor code"},
"setup.default.editor.cursor": {"icon":"","label":"Cursor","checked":"[[ \"$(omarchy-default-editor)\" == \"cursor\" ]]","action":"omarchy-default-editor cursor"},
@@ -167,49 +171,49 @@
"setup.default.editor.helix": {"icon":"","label":"Helix","checked":"[[ \"$(omarchy-default-editor)\" == \"helix\" ]]","action":"omarchy-default-editor helix"},
"setup.default.editor.vim": {"icon":"","label":"Vim","checked":"[[ \"$(omarchy-default-editor)\" == \"vim\" ]]","action":"omarchy-default-editor vim"},
"setup.default.editor.emacs": {"icon":"","label":"Emacs","checked":"[[ \"$(omarchy-default-editor)\" == \"emacs\" ]]","action":"omarchy-default-editor emacs"},
"setup.plugin": {"icon":"󰐱","label":"Plugins","aliases":["plugin","plugins"]},
"setup.plugin.enable": {"icon":"󰄬","label":"Enable Plugin","action":"omarchy-menu-plugin enable"},
"setup.plugin.disable": {"icon":"󰅖","label":"Disable Plugin","action":"omarchy-menu-plugin disable"},
"setup.plugin.add": {"icon":"󰖟","label":"Add Plugin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-plugin-add'"},
"setup.plugin.clone": {"icon":"󰆏","label":"Clone Plugin","action":"omarchy-menu-plugin clone"},
"setup.plugin.remove": {"icon":"󰭌","label":"Remove Plugin","when":"compgen -G \"$HOME/.config/omarchy/plugins/*/manifest.json\"","action":"omarchy-menu-plugin remove"},
"setup.security": {"icon":"","label":"Security"},
"setup.config": {"icon":"","label":"Config"},
"setup.security.fingerprint": {"icon":"󰈷","label":"Fingerprint","when":"omarchy-hw-fingerprint","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-security-fingerprint"},
"setup.plugin": {"icon":"󰐱","label":"插件","aliases":["plugin","plugins"]},
"setup.plugin.enable": {"icon":"󰄬","label":"启用插件","action":"omarchy-menu-plugin enable"},
"setup.plugin.disable": {"icon":"󰅖","label":"停用插件","action":"omarchy-menu-plugin disable"},
"setup.plugin.add": {"icon":"󰖟","label":"添加插件","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-plugin-add'"},
"setup.plugin.clone": {"icon":"󰆏","label":"克隆插件","action":"omarchy-menu-plugin clone"},
"setup.plugin.remove": {"icon":"󰭌","label":"移除插件","when":"compgen -G \"$HOME/.config/omarchy/plugins/*/manifest.json\"","action":"omarchy-menu-plugin remove"},
"setup.security": {"icon":"","label":"安全"},
"setup.config": {"icon":"","label":"配置"},
"setup.security.fingerprint": {"icon":"󰈷","label":"指纹","when":"omarchy-hw-fingerprint","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-security-fingerprint"},
"setup.security.fido2": {"icon":"","label":"Fido2","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-security-fido2"},
"setup.security.sshd": {"icon":"󰣀","label":"SSHD","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-security-sshd"},
"setup.security.passwordless-sudo": {"icon":"󰟵","label":"Passwordless Sudo","action":"omarchy-launch-floating-terminal-with-presentation omarchy-sudo-passwordless"},
"setup.security.sudoless-docker": {"icon":"󰡨","label":"Sudoless Docker","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-security-sudoless-docker"},
"setup.security.passwordless-sudo": {"icon":"󰟵","label":"免密 sudo","action":"omarchy-launch-floating-terminal-with-presentation omarchy-sudo-passwordless"},
"setup.security.sudoless-docker": {"icon":"󰡨","label":"免 sudo Docker","when":"omarchy-sudo-docker --configured","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-security-sudoless-docker"},
"setup.config.hyprland": {"icon":"","label":"Hyprland","action":"omarchy-launch-config-editor \"$HOME/.config/hypr/hyprland.lua\""},
"setup.config.hyprsunset": {"icon":"","label":"Hyprsunset","action":"omarchy-launch-config-editor ~/.config/hypr/hyprsunset.conf && omarchy-restart-hyprsunset"},
"setup.config.xcompose": {"icon":"󰞅","label":"XCompose","action":"omarchy-launch-config-editor ~/.XCompose && omarchy-restart-xcompose"},
"setup.direct-boot": {"icon":"","label":"Direct Boot","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-direct-boot"},
"setup.reset": {"icon":"󰑓","label":"Reset Computer","when":"[[ $(findmnt -no FSTYPE /) == btrfs ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-system-factory-reset"},
"setup.direct-boot": {"icon":"","label":"直接启动","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-direct-boot"},
"setup.reset": {"icon":"󰑓","label":"恢复出厂","when":"[[ $(findmnt -no FSTYPE /) == btrfs ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-system-factory-reset"},
// Install
"install.package": {"icon":"󰣇","label":"Package","action":"xdg-terminal-exec --app-id=org.omarchy.terminal omarchy-pkg-install"},
"install.package": {"icon":"󰣇","label":"软件包","action":"xdg-terminal-exec --app-id=org.omarchy.terminal omarchy-pkg-install"},
"install.aur": {"icon":"󰣇","label":"AUR","action":"xdg-terminal-exec --app-id=org.omarchy.terminal omarchy-pkg-aur-install"},
"install.ai": {"icon":"󱚤","label":"AI"},
"install.service": {"icon":"","label":"Service"},
"install.development": {"icon":"󰵮","label":"Development"},
"install.editor": {"icon":"","label":"Editor"},
"install.style": {"icon":"","label":"Style"},
"install.style.theme": {"icon":"󰸌","label":"Theme","action":"omarchy-launch-floating-terminal-with-presentation omarchy-theme-install"},
"install.style.background": {"icon":"","label":"Background","action":"omarchy-theme-bg-install"},
"install.style.font": {"icon":"","label":"Font"},
"install.service": {"icon":"","label":"服务"},
"install.development": {"icon":"󰵮","label":"开发环境"},
"install.editor": {"icon":"","label":"编辑器"},
"install.style": {"icon":"","label":"风格"},
"install.style.theme": {"icon":"󰸌","label":"主题","action":"omarchy-launch-floating-terminal-with-presentation omarchy-theme-install"},
"install.style.background": {"icon":"","label":"壁纸","action":"omarchy-theme-bg-install"},
"install.style.font": {"icon":"","label":"字体"},
"install.style.font.cascadia": {"icon":"","label":"Cascadia Mono","action":"omarchy-install-font 'Cascadia Mono' ttf-cascadia-mono-nerd 'CaskaydiaMono Nerd Font'"},
"install.style.font.meslo": {"icon":"","label":"Meslo LG Mono","action":"omarchy-install-font 'Meslo LG Mono' ttf-meslo-nerd 'MesloLGL Nerd Font'"},
"install.style.font.fira": {"icon":"","label":"Fira Code","action":"omarchy-install-font 'Fira Code' ttf-firacode-nerd 'FiraCode Nerd Font'"},
"install.style.font.victor": {"icon":"","label":"Victor Code","action":"omarchy-install-font 'Victor Code' ttf-victor-mono-nerd 'VictorMono Nerd Font'"},
"install.style.font.bitstream": {"icon":"","label":"Bitstream Vera Mono","action":"omarchy-install-font 'Bitstream Vera Code' ttf-bitstream-vera-mono-nerd 'BitstromWera Nerd Font'"},
"install.style.font.iosevka": {"icon":"","label":"Iosevka","action":"omarchy-install-font Iosevka ttf-iosevka-nerd 'Iosevka Nerd Font Mono'"},
"install.gaming": {"icon":"","label":"Gaming"},
"install.browser": {"icon":"","label":"Browser"},
"install.webapp": {"icon":"","label":"Web App","action":"omarchy-launch-floating-terminal-with-presentation omarchy-webapp-install"},
"install.terminal": {"icon":"","label":"Terminal"},
"install.gaming": {"icon":"","label":"游戏"},
"install.browser": {"icon":"","label":"浏览器"},
"install.webapp": {"icon":"","label":"网页应用","action":"omarchy-launch-floating-terminal-with-presentation omarchy-webapp-install"},
"install.terminal": {"icon":"","label":"终端"},
"install.tui": {"icon":"","label":"TUI","action":"omarchy-launch-floating-terminal-with-presentation omarchy-tui-install"},
"install.windows": {"icon":"󰍲","label":"Windows","disabled":"[[ -f $HOME/.local/share/applications/windows-vm.desktop ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-windows-vm install'"},
"install.preinstalls": {"icon":"󰏓","label":"Preinstalls","disabled":"[[ ! -f $HOME/.local/state/omarchy/preinstalls-removed ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-preinstalls"},
"install.preinstalls": {"icon":"󰏓","label":"预装应用","disabled":"[[ ! -f $HOME/.local/state/omarchy/preinstalls-removed ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-preinstalls"},
"install.browser.chrome": {"icon":"","label":"Chrome","disabled":"omarchy-pkg-present google-chrome","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser chrome'"},
"install.browser.edge": {"icon":"󰇩","label":"Edge","disabled":"omarchy-pkg-present microsoft-edge-stable-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser edge'"},
"install.browser.brave": {"icon":"","label":"Brave","disabled":"omarchy-pkg-present brave-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser brave'"},
@@ -224,7 +228,7 @@
"install.service.nordvpn": {"icon":"󱇱","label":"NordVPN","disabled":"omarchy-pkg-present nordvpn-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-service-nordvpn"},
"install.service.once": {"icon":"󰏖","label":"ONCE","disabled":"omarchy-pkg-present once-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-service-once"},
"install.service.bitwarden": {"icon":"󰟵","label":"Bitwarden","disabled":"omarchy-pkg-present bitwarden","action":"omarchy-install-and-launch Bitwarden 'bitwarden bitwarden-cli' bitwarden"},
"install.service.chromium-account": {"icon":"","label":"Chromium Account","when":"[[ -f ~/.config/chromium-flags.conf ]]","disabled":"grep -q oauth2-client-id ~/.config/chromium-flags.conf","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-chromium-google-account"},
"install.service.chromium-account": {"icon":"","label":"Chromium 账号","when":"[[ -f ~/.config/chromium-flags.conf ]]","disabled":"grep -q oauth2-client-id ~/.config/chromium-flags.conf","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-chromium-google-account"},
"install.editor.vscode": {"icon":"","label":"VSCode","disabled":"omarchy-pkg-present visual-studio-code-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-editor-vscode"},
"install.editor.cursor": {"icon":"","label":"Cursor","disabled":"omarchy-pkg-present cursor-bin","action":"omarchy-install-and-launch Cursor cursor-bin cursor"},
"install.editor.zed": {"icon":"","label":"Zed","disabled":"omarchy-pkg-present zed","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-editor-zed"},
@@ -237,23 +241,27 @@
"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.dictation": {"icon":"","label":"Dictation","disabled":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-install"},
"install.ai.deep-code": {"icon":"󱚤","label":"Deep Code","disabled":"omarchy-cmd-present deepcode","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-mise-install npm:@vegamo/deepcode-cli deepcode && deepcode'"},
"install.ai.dictation": {"icon":"","label":"语音听写","disabled":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-install"},
"install.ai.dim": {"icon":"󱚤","label":"Dim Agent","disabled":"omarchy-cmd-present dim","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-mise-install npm:dimcode dim && dim'"},
"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.kimi-code": {"icon":"󱚤","label":"Kimi Code","disabled":"omarchy-cmd-present kimi","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-kimi-install && kimi'"},
"install.ai.lm-studio": {"icon":"","iconFont":"omarchy","label":"LM Studio","disabled":"omarchy-pkg-present lmstudio-bin","action":"omarchy-install-app 'LM Studio' lmstudio-bin"},
"install.ai.ollama": {"icon":"","iconFont":"omarchy","label":"Ollama","disabled":"omarchy-cmd-present ollama","action":"if omarchy-cmd-present nvidia-smi; then ollama_pkg=ollama-cuda; elif omarchy-cmd-present rocminfo; then ollama_pkg=ollama-rocm; else ollama_pkg=ollama; fi; omarchy-install-app Ollama \"$ollama_pkg\""},
"install.ai.t3-code": {"icon":"","iconFont":"omarchy","label":"T3 Code","disabled":"omarchy-pkg-present t3code-bin","action":"omarchy-install-and-launch 'T3 Code' t3code-bin t3code"},
"install.ai.cn-hub": {"icon":"󱚤","label":"国产模型组合 (AI Hub)","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-ai-setup"},
"install.gaming.steam": {"icon":"","label":"Steam","disabled":"omarchy-pkg-present steam","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-steam"},
"install.gaming.retroarch": {"icon":"󰯉","label":"RetroArch","disabled":"omarchy-pkg-present retroarch","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-retroarch"},
"install.gaming.minecraft": {"icon":"󰍳","label":"Minecraft","disabled":"omarchy-pkg-present minecraft-launcher","action":"omarchy-install-and-launch Minecraft minecraft-launcher minecraft-launcher"},
"install.gaming.geforce-now": {"icon":"󰢹","label":"NVIDIA GeForce NOW","disabled":"flatpak info com.nvidia.geforcenow","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-geforce-now"},
"install.gaming.xbox-cloud": {"icon":"","label":"Xbox Cloud Gaming","disabled":"[[ -f \"$HOME/.local/share/applications/Xbox Cloud Gaming.desktop\" ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-xbox-cloud"},
"install.gaming.xbox-controllers": {"icon":"󰂯","label":"Xbox Controllers","disabled":"omarchy-pkg-present xpadneo-dkms","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-xbox-controllers"},
"install.gaming.xbox-cloud": {"icon":"","label":"Xbox 云游戏","disabled":"[[ -f \"$HOME/.local/share/applications/Xbox Cloud Gaming.desktop\" ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-xbox-cloud"},
"install.gaming.xbox-controllers": {"icon":"󰂯","label":"Xbox 手柄","disabled":"omarchy-pkg-present xpadneo-dkms","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-xbox-controllers"},
"install.gaming.battlenet": {"icon":"","label":"Battle.net","disabled":"[[ -d $HOME/Games/battlenet ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-battlenet"},
"install.gaming.lutris": {"icon":"","label":"Lutris","disabled":"omarchy-pkg-present lutris","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-lutris"},
"install.gaming.heroic": {"icon":"󱓟","label":"Heroic (Epic Games)","disabled":"omarchy-pkg-present heroic-games-launcher-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-heroic"},
"install.gaming.retro-launcher": {"icon":"󰯉","label":"RetroArch Game Launcher","action":"omarchy-games-retro-install"},
"install.gaming.retro-launcher": {"icon":"󰯉","label":"RetroArch 启动器","action":"omarchy-games-retro-install"},
"install.development.rails": {"icon":"󰫏","label":"Ruby on Rails","disabled":"[[ -d $HOME/.local/share/mise/installs/ruby ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env ruby'"},
"install.development.docker-dbs": {"icon":"","label":"Docker DB","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-docker-dbs"},
"install.development.docker-dbs": {"icon":"","label":"Docker 数据库","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-docker-dbs"},
"install.development.javascript": {"icon":"","label":"JavaScript"},
"install.development.go": {"icon":"","label":"Go","disabled":"[[ -d $HOME/.local/share/mise/installs/go ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env go'"},
"install.development.php": {"icon":"","label":"PHP"},
@@ -276,22 +284,22 @@
"install.development.elixir.phoenix": {"icon":"","label":"Phoenix","disabled":"compgen -G \"$HOME/.mix/archives/phx_new*\"","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env phoenix'"},
// Remove
"remove.package": {"icon":"󰣇","label":"Package","action":"xdg-terminal-exec --app-id=org.omarchy.terminal omarchy-pkg-remove"},
"remove.ai": {"icon":"󱚤","label":"AI","title":"Remove"},
"remove.service": {"icon":"","label":"Services","title":"Remove"},
"remove.development": {"icon":"󰵮","label":"Development","title":"Remove"},
"remove.theme": {"icon":"󰸌","label":"Theme","action":"omarchy-theme-remove"},
"remove.gaming": {"icon":"","label":"Gaming","title":"Remove"},
"remove.browser": {"icon":"","label":"Browser","title":"Remove"},
"remove.webapp": {"icon":"","label":"Web App","when":"grep -qE '^Exec=.*(omarchy-launch-webapp|omarchy-webapp-handler)' $HOME/.local/share/applications/*.desktop","action":"omarchy-webapp-remove"},
"remove.package": {"icon":"󰣇","label":"软件包","action":"xdg-terminal-exec --app-id=org.omarchy.terminal omarchy-pkg-remove"},
"remove.ai": {"icon":"󱚤","label":"AI","title":"移除"},
"remove.service": {"icon":"","label":"服务","title":"移除"},
"remove.development": {"icon":"󰵮","label":"开发环境","title":"移除"},
"remove.theme": {"icon":"󰸌","label":"主题","action":"omarchy-theme-remove"},
"remove.gaming": {"icon":"","label":"游戏","title":"移除"},
"remove.browser": {"icon":"","label":"浏览器","title":"移除"},
"remove.webapp": {"icon":"","label":"网页应用","when":"grep -qE '^Exec=.*(omarchy-launch-webapp|omarchy-webapp-handler)' $HOME/.local/share/applications/*.desktop","action":"omarchy-webapp-remove"},
"remove.tui": {"icon":"","label":"TUI","when":"grep -qE '^Exec=.*(\\$TERMINAL|xdg-terminal-exec).*-e' $HOME/.local/share/applications/*.desktop","action":"omarchy-tui-remove"},
"remove.windows": {"icon":"󰍲","label":"Windows","when":"[[ -f $HOME/.local/share/applications/windows-vm.desktop ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-windows-vm remove'"},
"remove.preinstalls": {"icon":"󰏓","label":"Preinstalls","when":"[[ ! -f $HOME/.local/state/omarchy/preinstalls-removed ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-preinstalls"},
"remove.security": {"icon":"","label":"Security","title":"Remove"},
"remove.security.fingerprint": {"icon":"󰈷","label":"Fingerprint","when":"omarchy-pkg-present fprintd","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-security-fingerprint"},
"remove.preinstalls": {"icon":"󰏓","label":"预装应用","when":"[[ ! -f $HOME/.local/state/omarchy/preinstalls-removed ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-preinstalls"},
"remove.security": {"icon":"","label":"安全","title":"移除"},
"remove.security.fingerprint": {"icon":"󰈷","label":"指纹","when":"omarchy-pkg-present fprintd","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-security-fingerprint"},
"remove.security.fido2": {"icon":"","label":"Fido2","when":"omarchy-pkg-present pam-u2f","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-security-fido2"},
"remove.security.sshd": {"icon":"󰣀","label":"SSHD","when":"systemctl is-enabled --quiet sshd","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-security-sshd"},
"remove.security.sudoless-docker": {"icon":"󰡨","label":"Sudoless Docker","when":"id -nG | grep -qw docker","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-security-sudoless-docker"},
"remove.security.sudoless-docker": {"icon":"󰡨","label":"免 sudo Docker","when":"! omarchy-sudo-docker --configured","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-security-sudoless-docker"},
"remove.browser.chrome": {"icon":"","label":"Chrome","when":"omarchy-pkg-present google-chrome","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-browser chrome'"},
"remove.browser.edge": {"icon":"󰇩","label":"Edge","when":"omarchy-pkg-present microsoft-edge-stable-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-browser edge'"},
"remove.browser.brave": {"icon":"","label":"Brave","when":"omarchy-pkg-present brave-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-browser brave'"},
@@ -301,7 +309,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.dictation": {"icon":"","label":"Dictation","when":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-remove"},
"remove.ai.dictation": {"icon":"","label":"语音听写","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"},
"remove.ai.ollama": {"icon":"","iconFont":"omarchy","label":"Ollama","when":"omarchy-pkg-present ollama","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-ollama"},
@@ -310,17 +318,17 @@
"remove.gaming.retroarch": {"icon":"","label":"RetroArch","when":"omarchy-pkg-present retroarch","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-gaming-retroarch"},
"remove.gaming.minecraft": {"icon":"󰍳","label":"Minecraft","when":"omarchy-pkg-present minecraft-launcher","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-gaming-minecraft"},
"remove.gaming.geforce-now": {"icon":"󰢹","label":"NVIDIA GeForce NOW","when":"flatpak info com.nvidia.geforcenow","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-gaming-geforce-now"},
"remove.gaming.xbox-cloud": {"icon":"","label":"Xbox Cloud Gaming","when":"[[ -f \"$HOME/.local/share/applications/Xbox Cloud Gaming.desktop\" ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-gaming-xbox-cloud"},
"remove.gaming.xbox-controllers": {"icon":"󰖺","label":"Xbox Controllers (󰂯)","when":"omarchy-pkg-present xpadneo-dkms","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-gaming-xbox-controllers"},
"remove.gaming.xbox-cloud": {"icon":"","label":"Xbox 云游戏","when":"[[ -f \"$HOME/.local/share/applications/Xbox Cloud Gaming.desktop\" ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-gaming-xbox-cloud"},
"remove.gaming.xbox-controllers": {"icon":"󰖺","label":"Xbox 手柄 (󰂯)","when":"omarchy-pkg-present xpadneo-dkms","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-gaming-xbox-controllers"},
"remove.gaming.battlenet": {"icon":"","label":"Battle.net","when":"[[ -d $HOME/Games/battlenet ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-gaming-battlenet"},
"remove.gaming.lutris": {"icon":"","label":"Lutris","when":"omarchy-pkg-present lutris","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-gaming-lutris"},
"remove.gaming.heroic": {"icon":"󱓟","label":"Heroic (Epic Games)","when":"omarchy-pkg-present heroic-games-launcher-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-gaming-heroic"},
"remove.development.rails": {"icon":"󰫏","label":"Ruby on Rails","when":"[[ -d $HOME/.local/share/mise/installs/ruby ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-dev-env ruby'"},
"remove.development.javascript": {"icon":"","label":"JavaScript","title":"Remove"},
"remove.development.javascript": {"icon":"","label":"JavaScript","title":"移除"},
"remove.development.go": {"icon":"","label":"Go","when":"[[ -d $HOME/.local/share/mise/installs/go ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-dev-env go'"},
"remove.development.php": {"icon":"","label":"PHP","title":"Remove"},
"remove.development.php": {"icon":"","label":"PHP","title":"移除"},
"remove.development.python": {"icon":"","label":"Python","when":"[[ -d $HOME/.local/share/mise/installs/python ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-dev-env python'"},
"remove.development.elixir": {"icon":"","label":"Elixir","title":"Remove"},
"remove.development.elixir": {"icon":"","label":"Elixir","title":"移除"},
"remove.development.zig": {"icon":"","label":"Zig","when":"[[ -d $HOME/.local/share/mise/installs/zig ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-dev-env zig'"},
"remove.development.rust": {"icon":"","label":"Rust","when":"[[ -d $HOME/.rustup ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-dev-env rust'"},
"remove.development.java": {"icon":"","label":"Java","when":"[[ -d $HOME/.local/share/mise/installs/java ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-dev-env java'"},
@@ -339,16 +347,16 @@
// Update
"update.omarchy": {"icon":"","iconFont":"omarchy","label":"Omarchy","action":"omarchy-launch-floating-terminal-with-presentation omarchy-update"},
"update.channel": {"icon":"󰔫","label":"Channel"},
"update.config": {"icon":"","label":"Config","title":"Reset to default"},
"update.themes": {"icon":"󰸌","label":"Extra Themes","when":"omarchy-theme-extras","action":"omarchy-launch-floating-terminal-with-presentation omarchy-theme-update"},
"update.process": {"icon":"","label":"Process","title":"Restart"},
"update.hardware": {"icon":"󰇅","label":"Hardware","title":"Restart"},
"update.firmware": {"icon":"","label":"Firmware","action":"omarchy-launch-floating-terminal-with-presentation omarchy-update-firmware"},
"update.password": {"icon":"","label":"Password"},
"update.timezone": {"icon":"","label":"Timezone","action":"omarchy-menu-timezone"},
"update.time": {"icon":"","label":"Time","action":"omarchy-launch-floating-terminal-with-presentation omarchy-update-time"},
"update.channel.stable": {"icon":"🟢","label":"Stable","checked":"[[ \"$(omarchy-channel-current)\" == \"stable\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-channel-set stable'"},
"update.channel": {"icon":"󰔫","label":"通道"},
"update.config": {"icon":"","label":"配置","title":"重置为默认"},
"update.themes": {"icon":"󰸌","label":"附加主题","when":"omarchy-theme-extras","action":"omarchy-launch-floating-terminal-with-presentation omarchy-theme-update"},
"update.process": {"icon":"","label":"进程","title":"重启"},
"update.hardware": {"icon":"󰇅","label":"硬件","title":"重启"},
"update.firmware": {"icon":"","label":"固件","action":"omarchy-launch-floating-terminal-with-presentation omarchy-update-firmware"},
"update.password": {"icon":"","label":"密码"},
"update.timezone": {"icon":"","label":"时区","action":"omarchy-menu-timezone"},
"update.time": {"icon":"","label":"时间","action":"omarchy-launch-floating-terminal-with-presentation omarchy-update-time"},
"update.channel.stable": {"icon":"🟢","label":"稳定 Stable","checked":"[[ \"$(omarchy-channel-current)\" == \"stable\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-channel-set stable'"},
"update.channel.rc": {"icon":"🟡","label":"RC","checked":"[[ \"$(omarchy-channel-current)\" == \"rc\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-channel-set rc'"},
"update.channel.edge": {"icon":"🟠","label":"Edge","checked":"[[ \"$(omarchy-channel-current)\" == \"edge\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-channel-set edge'"},
"update.channel.dev": {"icon":"🔴","label":"Dev","checked":"[[ \"$(omarchy-channel-current)\" == \"dev\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-channel-set dev'"},
@@ -359,39 +367,54 @@
"update.config.plymouth": {"icon":"󱣴","label":"Plymouth","action":"omarchy-launch-floating-terminal-with-presentation omarchy-refresh-plymouth"},
"update.config.tmux": {"icon":"","label":"Tmux","action":"omarchy-launch-floating-terminal-with-presentation omarchy-refresh-tmux"},
"update.config.shell": {"icon":"󰍜","label":"Shell","action":"omarchy-launch-floating-terminal-with-presentation omarchy-refresh-shell"},
"update.hardware.audio": {"icon":"","label":"Audio","action":"omarchy-launch-floating-terminal-with-presentation omarchy-restart-audio"},
"update.hardware.audio": {"icon":"","label":"音频","action":"omarchy-launch-floating-terminal-with-presentation omarchy-restart-audio"},
"update.hardware.wifi": {"icon":"󱚾","label":"Wi-Fi","action":"omarchy-launch-floating-terminal-with-presentation omarchy-restart-wifi"},
"update.hardware.bluetooth": {"icon":"󰂯","label":"Bluetooth","action":"omarchy-launch-floating-terminal-with-presentation omarchy-restart-bluetooth"},
"update.hardware.trackpad": {"icon":"󰟸","label":"Trackpad","action":"omarchy-launch-floating-terminal-with-presentation omarchy-restart-trackpad"},
"update.password.drive": {"icon":"","label":"Drive Encryption","action":"omarchy-launch-floating-terminal-with-presentation omarchy-drive-password"},
"update.password.user": {"icon":"","label":"User","action":"omarchy-launch-floating-terminal-with-presentation passwd"},
"update.hardware.bluetooth": {"icon":"󰂯","label":"蓝牙","action":"omarchy-launch-floating-terminal-with-presentation omarchy-restart-bluetooth"},
"update.hardware.trackpad": {"icon":"󰟸","label":"触控板","action":"omarchy-launch-floating-terminal-with-presentation omarchy-restart-trackpad"},
"update.password.drive": {"icon":"","label":"磁盘加密","action":"omarchy-launch-floating-terminal-with-presentation omarchy-drive-password"},
"update.password.user": {"icon":"","label":"用户","action":"omarchy-launch-floating-terminal-with-presentation passwd"},
// OmarchyCN
"omarchycn": {"icon":"","label":"OmarchyCN"},
"omarchycn.mirrors": {"icon":"󰇧","label":"镜像 Mirrors"},
"omarchycn.mirrors.china": {"icon":"","label":"中国镜像 China","checked":"[[ \"$(cat ~/.config/omarchycn/mirror-profile 2>/dev/null)\" == china ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-mirror-apply china'"},
"omarchycn.mirrors.official": {"icon":"󰇩","label":"官方镜像 Official","checked":"[[ \"$(cat ~/.config/omarchycn/mirror-profile 2>/dev/null)\" == official ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-mirror-apply official'"},
"omarchycn.mirrors.benchmark": {"icon":"󰓅","label":"测速 Benchmark","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-mirror-benchmark"},
"omarchycn.mirrors.dev-china": {"icon":"","label":"开发工具国内源 Dev China","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-dev-mirror-apply china'"},
"omarchycn.mirrors.dev-official": {"icon":"","label":"开发工具官方源 Dev Official","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-dev-mirror-apply official'"},
"omarchycn.mirrors.restore": {"icon":"󰦛","label":"恢复 Restore","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-mirror-restore"},
"omarchycn.chinese": {"icon":"󰗊","label":"中文环境 Chinese"},
"omarchycn.ai": {"icon":"󱚤","label":"AI Hub"},
"omarchycn.ai.setup": {"icon":"","label":"配置向导","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-ai-setup"},
"omarchycn.ai.profiles": {"icon":"","label":"配置列表","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-ai-profile-list"},
"omarchycn.ai.test": {"icon":"󰙨","label":"连接测试","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-ai-test"},
"omarchycn.ai.doctor": {"icon":"󰨮","label":"AI 诊断","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-ai-doctor"},
"omarchycn.apps": {"icon":"󰀻","label":"国内应用"},
"omarchycn.apps.wechat": {"icon":"","label":"微信","disabled":"omarchy-pkg-present wechat-universal-bwrap","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-app-install wechat'"},
"omarchycn.apps.qq": {"icon":"","label":"QQ","disabled":"omarchy-pkg-present linuxqq","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-app-install qq'"},
"omarchycn.apps.feishu": {"icon":"","label":"飞书","disabled":"omarchy-pkg-present feishu-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-app-install feishu'"},
"omarchycn.apps.dingtalk": {"icon":"","label":"钉钉","disabled":"omarchy-pkg-present dingtalk-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-app-install dingtalk'"},
"omarchycn.apps.wemeet": {"icon":"","label":"腾讯会议","disabled":"omarchy-pkg-present wemeet-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-app-install wemeet'"},
"omarchycn.apps.wps": {"icon":"","label":"WPS Office","disabled":"omarchy-pkg-present wps-office-cn","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-app-install wps'"},
"omarchycn.apps.tencent-docs": {"icon":"","label":"腾讯文档","disabled":"[[ -f \"$HOME/.local/share/applications/腾讯文档.desktop\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-app-install tencent-docs'"},
"omarchycn.apps.yuque": {"icon":"","label":"语雀","disabled":"[[ -f \"$HOME/.local/share/applications/语雀.desktop\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-app-install yuque'"},
"omarchycn.apps.shimo": {"icon":"","label":"石墨文档","disabled":"[[ -f \"$HOME/.local/share/applications/石墨文档.desktop\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-app-install shimo'"},
"omarchycn.mirrors": {"icon":"󰇧","label":"镜像源"},
"omarchycn.mirrors.china": {"icon":"","label":"中国镜像","checked":"[[ \"$(cat ~/.config/omarchycn/mirror-profile 2>/dev/null)\" == china ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-mirror-apply china'"},
"omarchycn.mirrors.official": {"icon":"󰇩","label":"官方镜像","checked":"[[ \"$(cat ~/.config/omarchycn/mirror-profile 2>/dev/null)\" == official ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-mirror-apply official'"},
"omarchycn.mirrors.benchmark": {"icon":"󰓅","label":"测速","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-mirror-benchmark"},
"omarchycn.mirrors.dev-china": {"icon":"","label":"开发工具国内源","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-dev-mirror-apply china'"},
"omarchycn.mirrors.dev-official": {"icon":"","label":"开发工具官方源","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-dev-mirror-apply official'"},
"omarchycn.mirrors.restore": {"icon":"󰦛","label":"恢复","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-mirror-restore"},
"omarchycn.chinese": {"icon":"󰗊","label":"中文环境"},
"omarchycn.chinese.ime": {"icon":"󰌌","label":"输入法 Fcitx5+Rime","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-ime-apply"},
"omarchycn.chinese.hotkey-ctrl": {"icon":"󰌏","label":"切换键 Ctrl+Space","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-ime-hotkey ctrl-space'"},
"omarchycn.chinese.hotkey-super": {"icon":"󰌏","label":"切换键 Super+Space","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-ime-hotkey super-space'"},
"omarchycn.chinese.fonts": {"icon":"","label":"中文字体 Fonts","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-font-apply"},
"omarchycn.chinese.fonts": {"icon":"","label":"中文字体","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-font-apply"},
"omarchycn.chinese.locale": {"icon":"","label":"中文 Locale","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-locale-apply"},
"omarchycn.display": {"icon":"󰍹","label":"显示缩放 Scale"},
"omarchycn.display": {"icon":"󰍹","label":"显示缩放"},
"omarchycn.display.s10": {"icon":"󰍹","label":"1.0","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-display-scale 1.0'"},
"omarchycn.display.s125": {"icon":"󰍹","label":"1.25","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-display-scale 1.25'"},
"omarchycn.display.s15": {"icon":"󰍹","label":"1.5","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-display-scale 1.5'"},
"omarchycn.display.s16": {"icon":"󰍹","label":"1.6","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-display-scale 1.6'"},
"omarchycn.display.s175": {"icon":"󰍹","label":"1.75","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-display-scale 1.75'"},
"omarchycn.display.s20": {"icon":"󰍹","label":"2.0","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-display-scale 2.0'"},
"omarchycn.diagnostics": {"icon":"󰨮","label":"诊断 Diagnostics"},
"omarchycn.diagnostics.doctor": {"icon":"󰨮","label":"System Doctor","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-doctor all'"},
"omarchycn.diagnostics.mirror-fix": {"icon":"󰇧","label":"镜像修复 Mirror Fix","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-doctor mirror --fix'"},
"omarchycn.diagnostics.ime": {"icon":"󰌌","label":"输入法状态 IME Status","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-ime-status"},
"omarchycn.update": {"icon":"","label":"更新 Update","when":"[[ -f ~/.local/state/omarchycn/overlay-manifest ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-update"},
"omarchycn.about": {"icon":"","label":"关于 About","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-status"},
"omarchycn.diagnostics": {"icon":"󰨮","label":"诊断"},
"omarchycn.diagnostics.doctor": {"icon":"󰨮","label":"系统诊断","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-doctor all'"},
"omarchycn.diagnostics.mirror-fix": {"icon":"󰇧","label":"镜像修复","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-doctor mirror --fix'"},
"omarchycn.diagnostics.ime": {"icon":"󰌌","label":"输入法状态","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-ime-status"},
"omarchycn.update": {"icon":"","label":"更新","when":"[[ -f ~/.local/state/omarchycn/overlay-manifest ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-update"},
"omarchycn.about": {"icon":"","label":"关于","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-status"},
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

+1 -2
View File
@@ -207,8 +207,7 @@ It only does the things `/etc/skel` can't:
and `~/.config/gtk-3.0/bookmarks` (needs `$HOME` expansion).
- Hyprland's package-owned default input reads `XKBLAYOUT` / `XKBVARIANT`
from `/etc/vconsole.conf`; no per-user Hyprland config rewrite is needed.
- `xdg-settings set default-web-browser chromium.desktop` and
`xdg-mime default HEY.desktop x-scheme-handler/mailto` (XDG-aware paths).
- `xdg-settings set default-web-browser chromium.desktop` (XDG-aware path).
- `omarchy-refresh-applications` (composes generated `.desktop` launchers).
- Sources `install/user/all.sh` — theme, chromium, git, xcompose, mise,
keyring, per-user hardware quirks (asus mic/mixer, framework f13 audio, …).
+30
View File
@@ -0,0 +1,30 @@
# Release Checklist
每次发布 `<upstream>-cn.<n>` 按序执行,任一步失败即停止:
1. `./test/all`:相对上游基线零新增失败;`test/shell.d/omarchycn-*.sh` 全绿
2.`--local-source` 重建 ISO`packages/omarchy-pkgs-cn.patch` 已应用到 omarchy-pkgs
3. 验证 omarchy-dev 包含 cn 层(`tar -tf … | grep usr/share/omarchy/cn/`
4. QEMU OVMF UEFI 冒烟:进入安装器欢迎屏
5. `sha256sum``SHA256SUMS.txt`;签名 SUMS 与 ISO(发布子钥)
6. SBOM 两份:syftlive airootfs+ 离线仓库 .PKGINFO 采集
7. `release.json`(版本、双向提交、包版本表、迁移列表、min_compatible、产物清单)→ 签名 release.json
8. 建 tag 与 Release,上传全部产物,Release Notes 写明上游基线与已知问题
9. 匿名回读已发布 ISO 并 sha256 复核 == 本地构建值
10. `cn/release` 数字 +1,提交
## 版本规则
`<omarchy-upstream-version>-cn.<n>``min_compatible` 只在有破坏性迁移时前移。
# 上游同步 SLA
- `upstream-sync.yml` 每日拉取 basecamp/omarchy quattro,自动开同步 PR(含试合并冲突标注)
- 常规变更:7 天内完成审查合并;上游安全修复:48 小时内
- 合并后必须重跑第 1 步测试门禁
# 安全响应
- 接报渠道见 `SECURITY.md`72 小时内确认
- 涉及发布密钥泄露:按 `docs/release-signing.md` 轮换流程处理,吊销并公告
- 修复发布走本清单完整流程,不走捷径
+3 -3
View File
@@ -1,7 +1,6 @@
# Omarchy core package list pacstrapped by the ISO.
# The ISO builder also reads this file when constructing the offline mirror.
aether
alsa-utils
asdcontrol
avahi
@@ -34,8 +33,11 @@ eza
fakeroot
fastfetch
fcitx5
fcitx5-chinese-addons
fcitx5-configtool
fcitx5-gtk
fcitx5-qt
fcitx5-rime
fd
ffmpegthumbnailer
fontconfig
@@ -71,7 +73,6 @@ less
libsecret
libvips
libyaml
libreoffice-fresh
llvm
localsend
lua51
@@ -90,7 +91,6 @@ noto-fonts-cjk
noto-fonts-emoji
nss-mdns
nvim
obs-studio
obsidian
omacalc
omacut
+58 -15
View File
@@ -24,6 +24,49 @@
OMARCHY_FORM_BACK=1
OMARCHY_FORM_SIGNAL=130
# Prompt strings; the ISO's graphical console exports OMARCHY_INSTALL_LANG=zh
if [[ ${OMARCHY_INSTALL_LANG:-} == zh ]]; then
OMARCHY_L_KEYBOARD_HEADER="选择键盘布局"
OMARCHY_L_USERNAME_PROMPT="用户名> "
OMARCHY_L_USERNAME_HINT="仅限字母数字、不含空格(如 dhh)"
OMARCHY_L_USERNAME_RESERVED="该用户名已被系统保留"
OMARCHY_L_USERNAME_TAKEN="该用户名在本机已存在"
OMARCHY_L_USERNAME_INVALID="用户名必须为字母数字且不含空格"
OMARCHY_L_PASSWORD_PROMPT="密码> "
OMARCHY_L_PASSWORD_HINT="用于用户与 root,启用加密时也用于磁盘解锁"
OMARCHY_L_CONFIRM_PROMPT="确认密码> "
OMARCHY_L_CONFIRM_HINT="需与刚输入的密码一致"
OMARCHY_L_PASSWORD_BLANK="密码不能为空!"
OMARCHY_L_PASSWORD_MISMATCH="两次输入的密码不一致!"
OMARCHY_L_FULLNAME_PROMPT="姓名> "
OMARCHY_L_IDENTITY_HINT="用于 git 身份(回车跳过)"
OMARCHY_L_EMAIL_PROMPT="邮箱> "
OMARCHY_L_HOSTNAME_PROMPT="主机名> "
OMARCHY_L_HOSTNAME_HINT="字母、数字与连字符(回车默认 omarchy)"
OMARCHY_L_HOSTNAME_INVALID="主机名须为 1-63 位字母/数字/连字符,且不能以连字符开头或结尾"
OMARCHY_L_TIMEZONE_HEADER="时区"
else
OMARCHY_L_KEYBOARD_HEADER="Select keyboard layout"
OMARCHY_L_USERNAME_PROMPT="Username> "
OMARCHY_L_USERNAME_HINT="Alphanumeric without spaces (like dhh)"
OMARCHY_L_USERNAME_RESERVED="Username is reserved for system"
OMARCHY_L_USERNAME_TAKEN="That username already exists on this machine"
OMARCHY_L_USERNAME_INVALID="Username must be alphanumeric with no spaces"
OMARCHY_L_PASSWORD_PROMPT="Password> "
OMARCHY_L_PASSWORD_HINT="Used for user + root, and disk encryption when enabled"
OMARCHY_L_CONFIRM_PROMPT="Confirm> "
OMARCHY_L_CONFIRM_HINT="Must match the password you just typed"
OMARCHY_L_PASSWORD_BLANK="Your password can't be blank!"
OMARCHY_L_PASSWORD_MISMATCH="Passwords didn't match!"
OMARCHY_L_FULLNAME_PROMPT="Full name> "
OMARCHY_L_IDENTITY_HINT="Used for git authentication (hit return to skip)"
OMARCHY_L_EMAIL_PROMPT="Email address> "
OMARCHY_L_HOSTNAME_PROMPT="Hostname> "
OMARCHY_L_HOSTNAME_HINT="Letters, digits, and dashes (or return for 'omarchy')"
OMARCHY_L_HOSTNAME_INVALID="Hostname must be 1-63 letters, digits, or dashes, and cannot start or end with a dash"
OMARCHY_L_TIMEZONE_HEADER="Timezone"
fi
# The English layouts lead, then everything else alphabetically. gum choose
# paginates in --height-sized pages and jumps to the page holding --selected,
# so an alphabetical English (US) landed deep enough to sit alone at the edge
@@ -94,7 +137,7 @@ omarchy_username_taken() { return 1; }
omarchy_prompt_keyboard() {
local choice status
choice=$(printf '%s\n' "$OMARCHY_KEYBOARD_LAYOUTS" | cut -d'|' -f1 |
gum choose --height 10 --selected "English (US)" --header "Select keyboard layout") && status=0 || status=$?
gum choose --height 10 --selected "English (US)" --header "$OMARCHY_L_KEYBOARD_HEADER") && status=0 || status=$?
((status == 0)) || return $status
keyboard_label="$choice"
@@ -104,19 +147,19 @@ omarchy_prompt_keyboard() {
omarchy_prompt_username() {
local status
while true; do
username=$(gum input --placeholder "Alphanumeric without spaces (like dhh)" --prompt.foreground="#845DF9" --prompt "Username> ") && status=0 || status=$?
username=$(gum input --placeholder "$OMARCHY_L_USERNAME_HINT" --prompt.foreground="#845DF9" --prompt "$OMARCHY_L_USERNAME_PROMPT") && status=0 || status=$?
((status == 0)) || return $status
if [[ "$username" =~ $OMARCHY_USERNAME_PATTERN ]]; then
if [[ "$username" =~ $OMARCHY_RESERVED_USERNAMES ]]; then
notice "Username is reserved for system" 1
notice "$OMARCHY_L_USERNAME_RESERVED" 1
elif omarchy_username_taken "$username"; then
notice "That username already exists on this machine" 1
notice "$OMARCHY_L_USERNAME_TAKEN" 1
else
return 0
fi
else
notice "Username must be alphanumeric with no spaces" 1
notice "$OMARCHY_L_USERNAME_INVALID" 1
fi
done
}
@@ -124,17 +167,17 @@ omarchy_prompt_username() {
omarchy_prompt_password() {
local status
while true; do
password=$(gum input --placeholder "Used for user + root, and disk encryption when enabled" --prompt.foreground="#845DF9" --password --prompt "Password> ") && status=0 || status=$?
password=$(gum input --placeholder "$OMARCHY_L_PASSWORD_HINT" --prompt.foreground="#845DF9" --password --prompt "$OMARCHY_L_PASSWORD_PROMPT") && status=0 || status=$?
((status == 0)) || return $status
password_confirmation=$(gum input --placeholder "Must match the password you just typed" --prompt.foreground="#845DF9" --password --prompt "Confirm> ") && status=0 || status=$?
password_confirmation=$(gum input --placeholder "$OMARCHY_L_CONFIRM_HINT" --prompt.foreground="#845DF9" --password --prompt "$OMARCHY_L_CONFIRM_PROMPT") && status=0 || status=$?
((status == 0)) || return $status
if [[ -n "$password" && "$password" == "$password_confirmation" ]]; then
return 0
elif [[ -z "$password" ]]; then
notice "Your password can't be blank!" 1
notice "$OMARCHY_L_PASSWORD_BLANK" 1
else
notice "Passwords didn't match!" 1
notice "$OMARCHY_L_PASSWORD_MISMATCH" 1
fi
done
}
@@ -143,16 +186,16 @@ omarchy_prompt_password() {
# only Esc/Ctrl+C end the prompt early.
omarchy_prompt_identity() {
local status
full_name=$(gum input --placeholder "Used for git authentication (hit return to skip)" --prompt.foreground="#845DF9" --prompt "Full name> ") && status=0 || status=$?
full_name=$(gum input --placeholder "$OMARCHY_L_IDENTITY_HINT" --prompt.foreground="#845DF9" --prompt "$OMARCHY_L_FULLNAME_PROMPT") && status=0 || status=$?
((status == 0)) || return $status
email_address=$(gum input --placeholder "Used for git authentication (hit return to skip)" --prompt.foreground="#845DF9" --prompt "Email address> ") && status=0 || status=$?
email_address=$(gum input --placeholder "$OMARCHY_L_IDENTITY_HINT" --prompt.foreground="#845DF9" --prompt "$OMARCHY_L_EMAIL_PROMPT") && status=0 || status=$?
return $status
}
omarchy_prompt_hostname() {
local status
while true; do
hostname=$(gum input --placeholder "Letters, digits, and dashes (or return for 'omarchy')" --prompt.foreground="#845DF9" --prompt "Hostname> ") && status=0 || status=$?
hostname=$(gum input --placeholder "$OMARCHY_L_HOSTNAME_HINT" --prompt.foreground="#845DF9" --prompt "$OMARCHY_L_HOSTNAME_PROMPT") && status=0 || status=$?
((status == 0)) || return $status
if [[ -z $hostname ]]; then
@@ -161,7 +204,7 @@ omarchy_prompt_hostname() {
elif [[ "$hostname" =~ $OMARCHY_HOSTNAME_PATTERN ]]; then
return 0
else
notice "Hostname must be 1-63 letters, digits, or dashes, and cannot start or end with a dash" 1
notice "$OMARCHY_L_HOSTNAME_INVALID" 1
fi
done
}
@@ -173,9 +216,9 @@ omarchy_prompt_timezone() {
guess=$(tzupdate -p 2>/dev/null) || guess=""
if [[ -n $guess ]]; then
timezone=$(timedatectl list-timezones | gum choose --height 10 --selected "$guess" --header "Timezone") && status=0 || status=$?
timezone=$(timedatectl list-timezones | gum choose --height 10 --selected "$guess" --header "$OMARCHY_L_TIMEZONE_HEADER") && status=0 || status=$?
else
timezone=$(timedatectl list-timezones | gum filter --height 10 --header "Timezone") && status=0 || status=$?
timezone=$(timedatectl list-timezones | gum filter --height 10 --header "$OMARCHY_L_TIMEZONE_HEADER") && status=0 || status=$?
fi
((status == 0)) || return $status
+1
View File
@@ -2,6 +2,7 @@ run_logged "$OMARCHY_INSTALL/user/theme.sh"
run_logged "$OMARCHY_INSTALL/user/chromium.sh"
run_logged "$OMARCHY_INSTALL/user/git.sh"
run_logged "$OMARCHY_INSTALL/user/xcompose.sh"
run_logged "$OMARCHY_INSTALL/user/cn-chinese.sh"
run_logged "$OMARCHY_INSTALL/user/mise-work.sh"
run_logged "$OMARCHY_INSTALL/user/hardware/asus/fix-audio-mixer.sh"
+14
View File
@@ -0,0 +1,14 @@
# Seed Chinese input and CJK font fallback on zh_CN systems.
grep -qs "^LANG=zh_CN" /etc/locale.conf || exit 0
if [[ ! -f ~/.config/fcitx5/profile ]]; then
mkdir -p ~/.config/fcitx5
cp "$OMARCHY_PATH/cn/fcitx5/profile" ~/.config/fcitx5/profile
fi
if [[ ! -f ~/.config/fontconfig/conf.d/64-omarchycn-cjk.conf ]]; then
mkdir -p ~/.config/fontconfig/conf.d
cp "$OMARCHY_PATH/cn/fontconfig/64-omarchycn-cjk.conf" ~/.config/fontconfig/conf.d/
fc-cache -f > /dev/null
fi
+2 -2
View File
@@ -3,7 +3,7 @@
set -e
if omarchy-done ensure voxtype-install-invitation; then
omarchy-notification-send -u critical -g  "Install Dictation with Voxtype" \
"Click to install voice dictation for Omarchy." \
omarchy-notification-send -u critical -g  "安装 Voxtype 语音听写" \
"点击为 Omarchy 安装语音听写。" \
--exec omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-install
fi
+2 -2
View File
@@ -5,7 +5,7 @@ set -e
# Omarchy ships no default agent, so invite once rather than picking one. An
# agent already chosen means the invitation has nothing to offer.
if [[ -z $(omarchy-default-agent) ]] && omarchy-done ensure agent-setup-invitation; then
omarchy-notification-send -u critical -g 󰚩 "Set your default agent" \
"Let your favorite agent help with Omarchy." \
omarchy-notification-send -u critical -g 󰚩 "设置默认 Agent" \
"让你常用的 Agent 帮你使用 Omarchy" \
--exec omarchy menu summon setup.default.agent
fi
@@ -6,7 +6,7 @@ set -e
# PAM file is the last thing the setup writes on success).
if omarchy-hw-fingerprint && [[ ! -f /etc/pam.d/omarchy-lock-fingerprint ]] &&
omarchy-done ensure fingerprint-setup-invitation; then
omarchy-notification-send -u critical -g 󰈷 "Setup Fingerprint Reader" \
"Enable sudo and unlocking with your fingerprint." \
omarchy-notification-send -u critical -g 󰈷 "设置指纹识别" \
"用指纹完成 sudo 与解锁。" \
--exec omarchy-launch-floating-terminal-with-presentation omarchy-setup-security-fingerprint
fi
+2 -2
View File
@@ -1,5 +1,5 @@
# Real newlines, not a literal \n: the card renders the body as it arrives, and
# elides past three lines.
omarchy-notification-send -u critical -g  "Learn Keybindings" \
$'Super + K for cheatsheet.\nSuper + Space for Omarchy Menu.' \
omarchy-notification-send -u critical -g  "学习快捷键" \
$'Super + K 查看快捷键表。\nSuper + Space 打开 Omarchy 菜单。' \
--exec omarchy-menu-keybindings
+2 -2
View File
@@ -1,10 +1,10 @@
notify_update() {
omarchy-notification-send -u critical -g  "Update System" "Click to update the system." \
omarchy-notification-send -u critical -g  "更新系统" "点击以更新系统。" \
--exec omarchy-launch-floating-terminal-with-presentation omarchy-update
}
notify_wifi() {
omarchy-notification-send -u critical -g 󰖩 "Setup Wi-Fi" "Click to configure the wireless network." \
omarchy-notification-send -u critical -g 󰖩 "设置 Wi-Fi" "点击以配置无线网络。" \
--exec omarchy-shell shell toggle omarchy.network
}
+8 -8
View File
@@ -1,10 +1,10 @@
▄▄▄
▄█████▄ ▄███████████▄ ▄███████ ▄███████ ▄███████ ▄█ █▄ ▄█ █▄
███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███
███ ███ ███ ███ ███ ███ ███ ███ ███ ███ █▀ ███ ███ ███ ███
███ ███ ███ ███ ███ ▄███▄▄▄███ ▄███▄▄▄██▀ ███ ▄███▄▄▄███▄ ███▄▄▄███
███ ███ ███ ███ ███ ▀███▀▀▀███ ▀███▀▀▀▀ ███ ▀▀███▀▀▀███ ▀▀▀▀▀▀███
███ ███ ███ ███ ███ ███ ███ ██████████ ███ █▄ ███ ███ ▄██ ███
███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███
▀█████▀ ▀█ ███ █▀ ███ █▀ ███ ███ ███████▀ ███ █▀ ▀█████▀
▄█████▄ ▄███████████▄ ▄███████ ▄███████ ▄███████ ▄█ █▄ ▄█ █▄ ▄███████ ▄█▄ ▄█▄
███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███▄ ███
███ ███ ███ ███ ███ ███ ███ ███ ███ ███ █▀ ███ ███ ███ ███ ███ █▀ ████▄ ███
███ ███ ███ ███ ███ ▄███▄▄▄███ ▄███▄▄▄██▀ ███ ▄███▄▄▄███▄ ███▄▄▄███ ███ ███▀█▄███
███ ███ ███ ███ ███ ▀███▀▀▀███ ▀███▀▀▀▀ ███ ▀▀███▀▀▀███ ▀▀▀▀▀▀███ ███ ███ ▀████
███ ███ ███ ███ ███ ███ ███ ██████████ ███ █▄ ███ ███ ▄██ ███ ███ █▄ ███ ▀███
███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███
▀█████▀ ▀█ ███ █▀ ███ █▀ ███ ███ ███████▀ ███ █▀ ▀█████▀ ███████▀ ▀█▀ ▀█▀
███ █▀
+1 -1
View File
@@ -1,6 +1,6 @@
# Welcome to Omarchy!
Omarchy is an [omakase](https://manuals.omamix.org/3/omacom/76/omakase-computing) Linux distribution based on [Arch](https://archlinux.org/), the tiling window manager [Hyprland](https://hypr.land/), and the desktop construction-kit [Quickshell](https://quickshell.org/). It ships with everything a modern, savvy computer user needs to be productive immediately. From [Neovim](https://neovim.io/) (btw) to Chromium, [Obsidian](https://obsidian.md/) to LibreOffice, and Kdenlive to OBS Studio. Hell, even a retro Winamp-style music player is there!
Omarchy is an [omakase](https://manuals.omamix.org/3/omacom/76/omakase-computing) Linux distribution based on [Arch](https://archlinux.org/), the tiling window manager [Hyprland](https://hypr.land/), and the desktop construction-kit [Quickshell](https://quickshell.org/). It ships with everything a modern, savvy computer user needs to be productive immediately. From [Neovim](https://neovim.io/) (btw) to Chromium, [Obsidian](https://obsidian.md/) to Kdenlive. Hell, even a retro Winamp-style music player is there!
This isn't just a grab bag of preinstalled packages, though. It's a complete system designed with both aesthetics and productivity in mind. Because a _beautiful_ system is a _motivating_ system, and productivity has always been [downstream from motivation](https://world.hey.com/dhh/beautiful-motivations-6fef7c73). There's zero bloat here: Just everything I use.
-9
View File
@@ -107,21 +107,12 @@ You can see all the main keyboard bindings with `Super + K` (Tmux bindings with
| `Super + Shift + Alt + M` | Music (cliamp) |
| `Super + Shift + /` | Password manager (1password) |
| `Super + Shift + N` | Editor (Neovim) |
| `Super + Shift + C` | Calendar ([HEY](https://hey.com/)) |
| `Super + Shift + E` | Email ([HEY](https://hey.com/)) |
| `Super + Shift + Alt + E` | New email ([HEY](https://hey.com/)) |
| `Super + Shift + A` | AI (ChatGPT) |
| `Super + Shift + Alt + A` | AI (Grok) |
| `Super + Shift + G` | Messenger (Signal) |
| `Super + Shift + P` | Google Photos |
| `Super + Shift + S` | Google Maps |
| `Super + Shift + Alt + G` | Messenger (WhatsApp) |
| `Super + Shift + Ctrl + G` | Messenger (Google) |
| `Super + Shift + D` | Docker (LazyDocker) |
| `Super + Shift + O` | Obsidian |
| `Super + Shift + W` | Writing (Omawrite) |
| `Super + Shift + X` | X |
| `Super + Shift + Alt + X` | X Compose |
| `Super + Shift + Y` | YouTube |
Change/add bindings in `~/.config/hypr/bindings.lua`.
+1 -19
View File
@@ -30,12 +30,6 @@ You start Omawrite with `Super + Shift + W`.
You start Pinta via the application launcher (`Super + Space`).
## Aether
[Aether](https://github.com/bjarneo/aether) is a theming application that can extract colors from a background image and turn them into a complete, cohesive theme. It's the easiest way to [make your own theme](43-making-your-own-theme.md).
You start Aether via the application launcher (`Super + Space`).
## LocalSend
[LocalSend](https://localsend.org/) lets you send files to other devices on the same network running the app, like Apple's AirDrop. It's cross-platform, though, so you can send files to and from Windows, macOS, Android, iOS, and of course Linux.
@@ -53,12 +47,6 @@ You can also send straight from the file manager: right-click any selection in N
Omarchy's firewall is closed by default except for LocalSend's port, so this works out of the box on a fresh install. See [security](48-security.md).
## LibreOffice
[LibreOffice](https://www.libreoffice.org/) is a complete office package with word processor, spreadsheet, presentations, drawing application, and more. It's compatible with files from Microsoft Office, so this is a great way to be able to open those Word documents.
You start LibreOffice via the application launcher (`Super + Space`).
## Omacalc
[Omacalc](https://github.com/omacom-io/omacalc) is Omarchy's own dead-simple calculator, which opens in a floating window.
@@ -77,15 +65,9 @@ You start Signal with `Super + Shift + G`. It's not part of the base install, so
You start mpv via the application launcher (`Super + Space`) or just double-click on a video in the file manager.
## OBS Studio
[OBS Studio](https://obsproject.com/) lets you record or stream video from multiple inputs. You can mix a screencast with a webcam with a microphone input. It's what was used to record the Omarchy screencasts.
You start OBS Studio via the application launcher (`Super + Space`).
## Kdenlive
[Kdenlive](https://kdenlive.org/) is an excellent video editor. Perfect for working on video that comes out of OBS Studio before sharing it.
[Kdenlive](https://kdenlive.org/) is an excellent video editor. Perfect for editing your screen recordings before sharing them.
You start Kdenlive via the application launcher (`Super + Space`).
-35
View File
@@ -14,18 +14,6 @@ When you're in a web app, you can copy the current URL to the clipboard using `S
By default, Omarchy already ships with an assortment of default apps:
## HEY
[HEY](https://www.hey.com/) is an email and calendar service that serves as a great alternative to people tired of Gmail, Outlook, or Apple Mail. It's made by [37signals](https://37signals.com/) where Omarchy originated.
You can start HEY Email using `Super + Shift + E`, jump straight to composing a new email using `Super + Shift + Alt + E`, and start HEY Calendar using `Super + Shift + C`.
## Basecamp
[Basecamp](https://basecamp.com/) is a project management service that helps small teams move faster and make more progress. Instead of patching together a mishmash of Trello, Slack, Asana, Notion, or whatever, you can have it all in one place with Basecamp. It's made by [37signals](https://37signals.com/) where Omarchy originated.
You can start Basecamp using the application launcher (`Super + Space`)
## ChatGPT
[ChatGPT](https://chatgpt.com) is the most popular AI chat bot in the world.
@@ -38,24 +26,6 @@ You can start ChatGPT using `Super + Shift + A`.
You can start Grok using `Super + Shift + Alt + A`.
## WhatsApp
[WhatsApp](https://www.whatsapp.com/) is one of the most popular messaging services in the world, and the web version is a great option for Linux.
You can start WhatsApp using `Super + Shift + Alt + G`.
## Google apps
Google Messages, Google Photos, Google Maps, and Google Contacts are all included as web apps too.
You can start Google Messages using `Super + Shift + Ctrl + G`, Google Photos using `Super + Shift + P`, and Google Maps using `Super + Shift + S`. Google Contacts is available through the app launcher (`Super + Space`).
## X
X is where news break.
You can start X using `Super + Shift + X` and go straight to writing a new post with `Super + Shift + Alt + X`.
## YouTube
[YouTube](https://youtube.com/) is the most popular video platform in the world.
@@ -68,8 +38,3 @@ You can start YouTube using `Super + Shift + Y`.
You start Zoom using the application launcher (`Super + Space`).
## Discord
[Discord](https://discord.com/) is where most gaming and open source communities hang out, including [Omarchy's own](https://discord.gg/tXFUdasqhY).
You start Discord using the application launcher (`Super + Space`).
+1 -1
View File
@@ -4,7 +4,7 @@ You can add your own themes to `~/.config/omarchy/themes`. Just copy one of the
The main file you have to tweak is `colors.toml`. That defines the color set that's then used to generate configurations for the terminal (Foot/Alacritty/Ghostty/Kitty), btop, Chromium, Hyprland, Neovim, Helix, VSCode, Obsidian, and the entire Omarchy shell (top bar, menu, notifications, OSD, and lock screen).
You can also use the included Aether application to create a new theme using a lovely GUI interface to play with colors and search for backgrounds. Just start it via the apps menu on `Super + Alt + Space`.
You can also install the [Aether](https://github.com/bjarneo/aether) application (`omarchy-pkg-add aether`) to create a new theme using a lovely GUI interface to play with colors and search for backgrounds.
### What an installed theme can contain
+1 -1
View File
@@ -66,7 +66,7 @@ Remember that you have built-in brightness control in Omarchy for the Apple Disp
### How do I get rid of all the extra software?
If you don't want programs like Obsidian or LibreOffice or any of the other preinstalled stuff, you can very easily remove it.
If you don't want programs like Obsidian or any of the other preinstalled stuff, you can very easily remove it.
Run _Remove > Package_ to see every package that's installed. Then you can select any package you'd like to remove with tab, and start removing everything you've selected with return.
+35
View File
@@ -0,0 +1,35 @@
# 安装 OmarchyCN
## 下载与校验
从 [Releases](https://git.zacharyzhang.com/ZacharyZhang-NY/omarchycn/releases) 下载最新 ISO 及 `SHA256SUMS.txt``SHA256SUMS.txt.asc`
```bash
curl -sSf https://git.zacharyzhang.com/ZacharyZhang-NY/omarchycn/raw/branch/quattro/cn/keys/omarchycn-release.asc | gpg --import
gpg --verify SHA256SUMS.txt.asc SHA256SUMS.txt
sha256sum -c SHA256SUMS.txt
```
签名主钥指纹应为 `04490F065F6ADD262A7243506EDF7B8603B5D247`(详见 `docs/release-signing.md`)。
## 写盘与启动
Linux 用 `caligula``dd`Mac/Windows 用 balenaEtcher 写入 U 盘,UEFI 模式启动,按提示完成 Omarchy 安装(磁盘、用户、加密等流程与上游一致)。
## 首次进入桌面后
```bash
omarchycn setup
```
向导依次配置:语言、时区、显示缩放、中文 locale、中文字体、Fcitx5+Rime 输入法、输入法切换键、pacman 镜像、开发工具镜像、国内应用、AI Hub、隐私说明。每一步都可跳过,中断后重跑会从未完成的步骤继续。
## 现有 Omarchy 叠加安装(Overlay
```bash
git clone https://git.zacharyzhang.com/ZacharyZhang-NY/omarchycn.git
cd omarchycn && ./bin/omarchy-cn-install-overlay
omarchy cn version # 验证
```
卸载:`omarchy-cn-install-overlay --uninstall`。更新:`omarchycn update`
+37
View File
@@ -0,0 +1,37 @@
# 镜像管理
## pacman 镜像
```bash
omarchycn mirror benchmark # 测速:延迟、吞吐、同步新鲜度
omarchycn mirror apply china # 自动选最优中国镜像(1 主 + 2 备)
omarchycn mirror apply official # 回到官方全球源
omarchycn mirror pin tuna # 固定单一镜像
omarchycn mirror status # 当前策略与镜像列表
omarchycn mirror restore # 恢复最近一次备份
```
每次写入 `/etc/pacman.d/mirrorlist` 前自动生成带时间戳备份。候选列表在 `cn/mirrors.json`
主镜像失效时:
```bash
omarchycn doctor mirror --fix # 检测并自动切换到健康镜像
```
## 开发工具镜像
覆盖 npm、pip、cargo、go、gem、docker 六个生态:
```bash
omarchycn dev-mirror list # 当前各生态源
omarchycn dev-mirror apply china # 全部切国内
omarchycn dev-mirror apply official --target npm # 指定生态切官方
omarchycn dev-mirror set npm https://my.registry/ # 自定义
omarchycn dev-mirror doctor # 连通性检查
```
写入前备份到 `~/.local/state/omarchycn/backups/dev-mirror/<时间戳>/`
恢复:`omarchycn restore list` + `omarchycn restore config <时间戳>`(仅用户级文件)。
docker 目标写 `/etc/docker/daemon.json`(需 sudo,重启 docker 生效;系统文件不参与
自动恢复,回退用 `omarchycn dev-mirror apply official --target docker`)。
+34
View File
@@ -0,0 +1,34 @@
# 中文输入与字体
## 一键配置
```bash
omarchycn locale apply # 生成 zh_CN.UTF-8 / en_US.UTF-8
omarchycn font apply # 思源黑体/宋体 + emoji + fallback 优先级
omarchycn ime apply # Fcitx5 + Rime,默认拼音
```
字体配置保证中文优先落到简体字形(含无语言标签的 Chromium/Electron 路径),
日文/韩文标签内容仍使用各自原生变体。验证:`omarchycn font status`
## 切换键
默认 `Ctrl+Space`Fcitx5 原生)。改用 `Super+Space`
```bash
omarchycn ime hotkey super-space
```
此时 Omarchy 主菜单自动迁移到 `Super+Alt+Space`(原 Apps 菜单快捷键让位,
可从主菜单进入 Apps 或在 `~/.config/hypr/bindings.lua` 自行改绑)。
切回:`omarchycn ime hotkey ctrl-space`(自动移除迁移块)。
## 排查
```bash
omarchycn ime status # 包、profile、切换键冲突、进程状态
omarchycn doctor ime
```
上游已内置 fcitx 环境变量(`INPUT_METHOD/QT_IM_MODULE/XMODIFIERS/SDL_IM_MODULE`
`omarchy-fcitx5` 用户服务,OmarchyCN 不重复配置。
+36
View File
@@ -0,0 +1,36 @@
# AI Hub
Harness(工具)与 Provider(模型服务)分层管理,配置数据在 `cn/registry/`
## 快速开始
```bash
omarchycn ai setup # 向导:Harness → Provider → 模型 → API Key → 测试
omarchycn ai launch # 以默认 Profile 启动
```
支持组合(stable = 有 mock 回归覆盖的适配器):
| Harness | DeepSeek | Kimi | Z.AI/GLM |
|---|---|---|---|
| Claude Code | stable | stable | stable |
| Codex | stable | — | — |
| OpenCode | stable | — | — |
| Kimi Code | — | 官方 CLI 自管 | — |
| Deep Code | 官方 CLI 自管 | — | — |
## 手动操作
```bash
omarchycn ai secret set deepseek # 存 KeySecret Service → pass → 0600 文件)
omarchycn ai profile create work claude-code deepseek default-coding
omarchycn ai profile use work
omarchycn ai test work # 真实连通/鉴权/模型调用测试
omarchycn ai doctor # 安装、凭据、端点诊断
omarchycn ai default work # 映射到 Super+Shift+Ctrl+A / omarchy agent
```
`ai default` 把配置持久化进 Harness 自身文件(Claude Code 的 settings.json /
Codex 的 config.toml,均 0600 并保留你的其它设置),上游快捷键即以国内模型启动。
注意:`ai test` 消耗一次极小请求(max_tokens=8),需有效 API Key 与账户额度。
+36
View File
@@ -0,0 +1,36 @@
# 更新与恢复
## 更新
- ISO 安装:系统随上游 `omarchy update`;cn 层当前随新版 ISO 迭代([omarchycn] pacman 仓库暂只分发 keyringomarchy-dev 包上仓后将改为 pacman 更新)
- Overlay 安装:`omarchycn update`(拉取源码 → 重装 overlay → 执行未跑过的 cn 迁移)
## 备份位置
| 内容 | 位置 |
|---|---|
| pacman mirrorlist | `/etc/pacman.d/mirrorlist.omarchycn-bak-<时间戳>` |
| 开发工具镜像配置 | `~/.local/state/omarchycn/backups/dev-mirror/<时间戳>/` |
| 输入法 profile | `~/.local/state/omarchycn/backups/ime/<时间戳>/` |
| 显示缩放 | `~/.config/hypr/monitors.lua.omarchycn-prev` |
| Codex/Claude 配置 | 同目录 `*.omarchycn-bak-<时间戳>` |
## 恢复
```bash
omarchycn restore list # 列出全部备份
omarchycn restore config <时间戳> # 恢复开发工具镜像配置
omarchycn mirror restore # 恢复 pacman mirrorlist
```
系统级快照与回滚沿用上游 Btrfs + Snapper 机制(见上游手册 System snapshots)。
## 诊断
```bash
omarchycn doctor # network + mirror + dev-mirror + ime
omarchycn ai doctor
omarchycn status # 版本与配置概览
```
诊断只在本地输出,不上传任何数据。
+7 -5
View File
@@ -2,13 +2,15 @@ echo "Move this install to the opt-in docker group default (the group is root-eq
# The docker group grants passwordless root (a container can bind-mount / and
# rewrite the host), so Omarchy no longer puts users in it by default. Bring
# existing installs in line: remove this user from the group if present. It takes
# effect at next login, and the current session keeps working until then. Anyone
# who wants passwordless docker back can opt in, behind a warning, with
# existing installs in line: remove this user from the group if present. The
# change applies after a reboot, so it stays reachable until then. Anyone who
# wants passwordless docker back can opt in, behind a warning, with
# Setup > Security > Sudoless Docker. Reuses the removal command so there is one
# source of truth for the privileged change and its notice.
# source of truth for the privileged change and its notice; DEFER_REBOOT keeps
# it from prompting mid-update — omarchy-update-restart handles the reboot once
# the whole update has finished.
if id -nG "$USER" | grep -qw docker; then
omarchy-remove-security-sudoless-docker
OMARCHY_DEFER_REBOOT=1 omarchy-remove-security-sudoless-docker
fi
# The Docker app entry copied into ~/.local/share/applications used to run
+840
View File
@@ -0,0 +1,840 @@
diff --git a/builder/build-iso.sh b/builder/build-iso.sh
index db8daa2..5e4ca44 100755
--- a/builder/build-iso.sh
+++ b/builder/build-iso.sh
@@ -118,7 +118,7 @@ cp "/tmp/$NODE_FILENAME" "$build_cache_dir/airootfs/opt/packages/"
# The selected omarchy-settings package is needed here so its post_install hook
# drops Omarchy's plymouthd.conf into /etc/plymouth before mkarchiso builds the
# live initramfs.
-arch_packages=(linux-t2 git gum jq openssl plymouth ttfx tzupdate omarchy-keyring "$OMARCHY_SETTINGS_PACKAGE" lvm2 cryptsetup parted)
+arch_packages=(linux-t2 git gum jq openssl plymouth ttfx tzupdate omarchy-keyring "$OMARCHY_SETTINGS_PACKAGE" lvm2 cryptsetup parted cage foot noto-fonts-cjk)
printf '%s\n' "${arch_packages[@]}" >> "$build_cache_dir/packages.x86_64"
# The live ISO boots linux-t2 (see airootfs/etc/mkinitcpio.d/linux-t2.preset), so
diff --git a/configs/airootfs/root/.automated_script.sh b/configs/airootfs/root/.automated_script.sh
index 16904f8..f1ec36e 100644
--- a/configs/airootfs/root/.automated_script.sh
+++ b/configs/airootfs/root/.automated_script.sh
@@ -10,7 +10,29 @@
# - COLUMNS/LINES so gum picks up real terminal size
set -euo pipefail
-[[ $(tty) == /dev/tty1 ]] || exit 0
+[[ $(tty) == /dev/tty1 || -n ${OMARCHY_CN_GUI:-} ]] || exit 0
+
+# OmarchyCN: run the installer in a CJK-capable graphical console when KMS
+# allows; otherwise fall back to the original English VT flow.
+if [[ -z ${OMARCHY_CN_GUI:-} ]] && command -v cage > /dev/null 2>&1; then
+ export XDG_RUNTIME_DIR=/run/omarchy-gui
+ mkdir -p "$XDG_RUNTIME_DIR"
+ chmod 700 "$XDG_RUNTIME_DIR"
+ rm -f "$XDG_RUNTIME_DIR/gui-started"
+ gui_status=0
+ OMARCHY_CN_GUI=1 OMARCHY_INSTALL_LANG=zh WLR_RENDERER=pixman \
+ cage -- foot -F "$0" || gui_status=$?
+ if [[ -f $XDG_RUNTIME_DIR/gui-started ]]; then
+ # GUI 已运行:退出码属于安装器,不再回退英文流程
+ exit "$gui_status"
+ fi
+ echo "图形安装环境不可用,回退英文控制台 (falling back to the English VT installer)"
+ unset OMARCHY_INSTALL_LANG XDG_RUNTIME_DIR
+fi
+
+if [[ -n ${OMARCHY_CN_GUI:-} ]]; then
+ touch "$XDG_RUNTIME_DIR/gui-started"
+fi
export OMARCHY_MIRROR="$(cat /root/omarchy_mirror)"
if [[ -f /root/omarchy_iso_ref ]]; then
diff --git a/configs/airootfs/root/configurator b/configs/airootfs/root/configurator
index 4cd3db3..96c507e 100644
--- a/configs/airootfs/root/configurator
+++ b/configs/airootfs/root/configurator
@@ -9,6 +9,167 @@ OMARCHY_SETTINGS_PACKAGE="${OMARCHY_SETTINGS_PACKAGE:-omarchy-settings}"
LOGO_PATH="$OMARCHY_PATH/logo.txt"
+# Installer strings; the graphical console stage exports OMARCHY_INSTALL_LANG=zh
+if [[ ${OMARCHY_INSTALL_LANG:-} == zh ]]; then
+ SYS_LANG="zh_CN.UTF-8"
+ L_TAGLINE='华丽、现代、观点鲜明的 Linux —— 中国版'
+ L_HINT='按回车开始安装'
+ L_ABORTED='安装已中止'
+ L_RETRY='稍后可重新运行: ./.automated_script.sh'
+ L_OWNER_PREP='这将把本机准备给另一位使用者。'
+ L_OWNER_PREP2='系统现在安装,但初始设置延迟到首次开机进行。'
+ L_OWNER_CONFIRM='为另一位使用者准备这台机器?'
+ L_OWNER_YES='是,为他人准备'
+ L_OWNER_NO='否,继续本人设置'
+ L_USER_STEP='开始设置你的用户账户……'
+ L_SUMMARY_OK='以上信息正确吗?'
+ L_SUMMARY_NO='不对,去修改'
+ L_F_FIELD='项目'
+ L_F_VALUE='值'
+ L_F_USER='用户名'
+ L_F_PASS='密码'
+ L_F_NAME='姓名'
+ L_F_EMAIL='邮箱'
+ L_F_HOST='主机名'
+ L_F_TZ='时区'
+ L_F_KB='键盘'
+ L_F_SKIP='[已跳过]'
+ L_NOSPACE_STEP='磁盘 %s 可用空间不足'
+ L_NOSPACE_1='%s 可用空间 %sOmarchy 至少需要 32GB。'
+ L_NOSPACE_2='请用分区工具在该盘腾出至少 32GB,然后重试。'
+ L_NOSPACE_CONFIRM='返回安装方式选择?'
+ L_BACK='返回'
+ L_OPEN_PT='打开分区工具'
+ L_BL_STEP='检查 %s 上的 BitLocker'
+ L_BL_1='检测到 %s 存在 BitLocker 签名。'
+ L_BL_2='请在 Windows 中关闭 BitLocker 并等待解密完成后重试。'
+ L_BL_3='仅暂停 BitLocker 不够——磁盘仍处于加密状态。'
+ L_BL_ABORT='已中止:该磁盘启用了 BitLocker。'
+ L_EFI_STEP='检查 %s 上已有的 EFI 分区'
+ L_EFI_WIN='发现 Windows ESP 位于 %s——保持原样不动。'
+ L_EFI_OWN='Omarchy 将在空闲空间中创建自己的 EFI 分区。'
+ L_EFI_NEW='将在空闲空间中新建 EFI 分区。'
+ L_FREE_STEP='分析 %s 的空闲空间'
+ L_MKPART_STEP='在 %s 上创建分区'
+ L_LUKS_STEP='在 %s 上配置 LUKS2 加密'
+ L_BTRFS_STEP='创建 Btrfs 文件系统与子卷'
+ L_FREESAY='在 %s 空闲空间中安装 Omarchy。'
+ L_CTRLC_UNENC='按 Ctrl+C 改为不加密安装。'
+ L_YES_INSTALL='是,开始安装'
+ L_YES_NOENC='是,不加密安装'
+ L_NO_CHANGE='否,去修改'
+ L_CONFIRM_ON='确认安装到 %s'
+ L_DISK_STEP='选择要安装 Omarchy 的磁盘……'
+ L_DISK_HEADER='选择安装磁盘'
+ L_PT_STEP='%s 的分区工具'
+ L_PT_1='为 Omarchy 腾出未分配空间,然后写入更改并退出。'
+ L_PT_2='不要在这里创建 Omarchy 分区——目标区域保持为空闲空间即可。'
+ L_MODE_FULL='整盘安装'
+ L_MODE_FREE='空闲空间安装(保留现有数据)'
+ L_MODE_OTHER='换一块磁盘'
+ L_MODE_STEP='选择 Omarchy 的安装方式……'
+ L_MODE_HEADER='选择 %s 上的安装方式'
+ L_WIPE_WARN='磁盘将被完全覆写,数据无法恢复。'
+ L_WIPE_CONFIRM='确认覆写 %s'
+ L_TAGLINE_W=38
+ L_HINT_W=14
+ L_WELCOME='开始设置你的机器……'
+ L_OWNER_HINT='按 Ctrl+C 可改为「为他人准备这台机器」。'
+ L_CFDISK_CONFIRM='打开 %s 的分区工具?'
+ L_OPEN_CFDISK='打开 cfdisk'
+ L_DS_GPT='初始化 %s 的 GPT'
+ L_DS_ESPFLAG='标记分区 %s 为 ESP'
+ L_DS_WIPE='清除 %s 的残留签名'
+ L_DS_MKBTRFS='在 %s 创建 Btrfs 文件系统'
+ L_DS_MOUNT='挂载 %s'
+ L_DS_SUBVOL='创建子卷 %s'
+ L_DS_MOUNTROOT='挂载目标根分区'
+ L_DS_MOUNTHOME='挂载 /home'
+ L_DS_MOUNTLOG='挂载 /var/log'
+ L_DS_MOUNTCACHE='挂载软件包缓存'
+ L_DS_MKESP='在 %s 创建 ESP 文件系统'
+ L_DS_MOUNTESP='挂载 ESP'
+else
+ SYS_LANG="en_US.UTF-8"
+ L_TAGLINE='Beautiful, Modern & Opinionated Linux by DHH'
+ L_HINT='Press Return to Start Install'
+ L_ABORTED='Aborted installation'
+ L_RETRY='You can retry later by running: ./.automated_script.sh'
+ L_OWNER_PREP='This prepares the machine for another owner.'
+ L_OWNER_PREP2='The system installs now, but setup is delayed until first boot.'
+ L_OWNER_CONFIRM='Prepare this machine for another owner?'
+ L_OWNER_YES='Yes, prepare for another owner'
+ L_OWNER_NO='No, keep setting up'
+ L_USER_STEP='Let'\''s setup your user account...'
+ L_SUMMARY_OK='Does this look right?'
+ L_SUMMARY_NO='No, change it'
+ L_F_FIELD='Field'
+ L_F_VALUE='Value'
+ L_F_USER='Username'
+ L_F_PASS='Password'
+ L_F_NAME='Full name'
+ L_F_EMAIL='Email address'
+ L_F_HOST='Hostname'
+ L_F_TZ='Timezone'
+ L_F_KB='Keyboard'
+ L_F_SKIP='[Skipped]'
+ L_NOSPACE_STEP='Not enough free space on %s'
+ L_NOSPACE_1='%s has %s of usable free space; Omarchy needs at least 32GB.'
+ L_NOSPACE_2='Open the partition tool to free at least 32GB, then try again.'
+ L_NOSPACE_CONFIRM='Return to installation mode?'
+ L_BACK='Back'
+ L_OPEN_PT='Open partition tool'
+ L_BL_STEP='Checking for BitLocker on %s'
+ L_BL_1='BitLocker signature detected on %s.'
+ L_BL_2='Turn BitLocker off in Windows and wait for the drive to finish decrypting, then try again.'
+ L_BL_3='Suspending BitLocker is not enough — the drive stays encrypted.'
+ L_BL_ABORT='Aborted: BitLocker is enabled on this disk.'
+ L_EFI_STEP='Checking existing EFI partitions on %s'
+ L_EFI_WIN='Found a Windows ESP at %s — leaving it untouched.'
+ L_EFI_OWN='Omarchy will create its own dedicated EFI partition in free space.'
+ L_EFI_NEW='A new EFI partition will be created in free space.'
+ L_FREE_STEP='Analyzing free space on %s'
+ L_MKPART_STEP='Creating partitions on %s'
+ L_LUKS_STEP='Setting up LUKS2 on %s'
+ L_BTRFS_STEP='Creating Btrfs filesystem and subvolumes'
+ L_FREESAY='Install Omarchy in the %s of free space.'
+ L_CTRLC_UNENC='Press Ctrl+C for unencrypted install.'
+ L_YES_INSTALL='Yes, install'
+ L_YES_NOENC='Yes, install without encryption'
+ L_NO_CHANGE='No, change it'
+ L_CONFIRM_ON='Confirm installing on %s'
+ L_DISK_STEP='Let'\''s select where to install Omarchy...'
+ L_DISK_HEADER='Select install disk'
+ L_PT_STEP='Partition tool for %s'
+ L_PT_1='Create unallocated free space for Omarchy, then write changes and quit.'
+ L_PT_2='Do not create an Omarchy partition here — leave the target area as Free space.'
+ L_MODE_FULL='Full disk install'
+ L_MODE_FREE='Free space install (alongside existing data)'
+ L_MODE_OTHER='Choose a different disk'
+ L_MODE_STEP='Let'\''s select how to install Omarchy...'
+ L_MODE_HEADER='Select installation mode on %s'
+ L_WIPE_WARN='Everything will be overwritten. There is no recovery possible.'
+ L_WIPE_CONFIRM='Confirm overwriting %s'
+ L_TAGLINE_W=44
+ L_HINT_W=29
+ L_WELCOME="Let's setup your machine..."
+ L_OWNER_HINT='Press Ctrl+C to prepare this machine for another owner.'
+ L_CFDISK_CONFIRM='Open partition tool for %s?'
+ L_OPEN_CFDISK='Open cfdisk'
+ L_DS_GPT='initializing GPT on %s'
+ L_DS_ESPFLAG='flagging partition %s as ESP'
+ L_DS_WIPE='clearing stale signatures on %s'
+ L_DS_MKBTRFS='creating the Btrfs filesystem on %s'
+ L_DS_MOUNT='mounting %s'
+ L_DS_SUBVOL='creating subvolume %s'
+ L_DS_MOUNTROOT='mounting the target root'
+ L_DS_MOUNTHOME='mounting /home'
+ L_DS_MOUNTLOG='mounting /var/log'
+ L_DS_MOUNTCACHE='mounting the package cache'
+ L_DS_MKESP='creating the ESP filesystem on %s'
+ L_DS_MOUNTESP='mounting the ESP'
+fi
+
# The setup form — keyboard, account, hostname, and timezone questions, plus the
# rules their answers are checked against — shared verbatim with the first-boot
# owner setup that finishes a deferred install. build-iso.sh vendors it out of
@@ -122,12 +283,12 @@ greeter() {
printf '\033[%d;1H' "$logo_row"
gum style --foreground 2 --padding "0 0 0 $PADDING_LEFT" "$(<"$LOGO_PATH")"
- tagline="Beautiful, Modern & Opinionated Linux by DHH"
- tpad=$(((cols - ${#tagline}) / 2)); (( tpad < 0 )) && tpad=0
+ tagline="$L_TAGLINE"
+ tpad=$(((cols - L_TAGLINE_W) / 2)); (( tpad < 0 )) && tpad=0
printf '\033[%d;%dH%s' "$tagline_row" "$((tpad + 1))" "$tagline"
- hint="Press Return to Start Install"
- hpad=$(((cols - ${#hint}) / 2)); (( hpad < 0 )) && hpad=0
+ hint="$L_HINT"
+ hpad=$(((cols - L_HINT_W) / 2)); (( hpad < 0 )) && hpad=0
printf '\033[%d;%dH\033[2m%s\033[0m' "$hint_row" "$((hpad + 1))" "$hint"
# ColorShift the logo: a green base (indexed color 2) with a cyan accent (6)
@@ -176,9 +337,9 @@ greeter() {
}
abort() {
- gum style "${1:-Aborted installation}"
+ gum style "${1:-$L_ABORTED}"
echo
- gum style "You can retry later by running: ./.automated_script.sh"
+ gum style "$L_RETRY"
exit 1
}
@@ -212,8 +373,8 @@ keyboard_form() {
while true; do
clear_logo
echo
- say "Let's setup your machine..."
- say --foreground 8 "Press Ctrl+C to prepare this machine for another owner."
+ say "$L_WELCOME"
+ say --foreground 8 "$L_OWNER_HINT"
echo
omarchy_prompt_keyboard && status=0 || status=$?
@@ -245,11 +406,11 @@ keyboard_form() {
confirm_prepare_for_another_owner() {
clear_logo
echo
- say "This prepares the machine for another owner."
- say --foreground 8 "The system installs now, but setup is delayed until first boot."
+ say "$L_OWNER_PREP"
+ say --foreground 8 "$L_OWNER_PREP2"
echo
- gum confirm --affirmative "Yes, prepare for another owner" --negative "No, keep setting up" \
- "Prepare this machine for another owner?"
+ gum confirm --affirmative "$L_OWNER_YES" --negative "$L_OWNER_NO" \
+ "$L_OWNER_CONFIRM"
}
# The user step. Deferred-provisioning installs skip it entirely — the machine's first owner
@@ -257,7 +418,7 @@ confirm_prepare_for_another_owner() {
# operator sets nothing user-specific.
user_form() {
- step "Let's setup your user account..."
+ step "$L_USER_STEP"
# Each prompt reports 0 (set), OMARCHY_FORM_BACK (Esc), or OMARCHY_FORM_SIGNAL
# (Ctrl+C). Both non-zero cases unwind to user_step, which decides what they
@@ -291,18 +452,18 @@ user_step() {
fi
# Add manual padding since gum table -p doesn't respect padding
- echo -e "Field,Value
-Username,$username
-Password,$(printf "%${#password}s" | tr ' ' '*')
-Full name,${full_name:-[Skipped]}
-Email address,${email_address:-[Skipped]}
-Hostname,$hostname
-Timezone,$timezone
-Keyboard,$keyboard" |
+ echo -e "$L_F_FIELD,$L_F_VALUE
+$L_F_USER,$username
+$L_F_PASS,$(printf "%${#password}s" | tr ' ' '*')
+$L_F_NAME,${full_name:-$L_F_SKIP}
+$L_F_EMAIL,${email_address:-$L_F_SKIP}
+$L_F_HOST,$hostname
+$L_F_TZ,$timezone
+$L_F_KB,$keyboard" |
gum table -s "," -p | sed "s/^/${PADDING_LEFT_SPACES}/"
echo
- if gum confirm --negative "No, change it" "Does this look right?"; then
+ if gum confirm --negative "$L_SUMMARY_NO" "$L_SUMMARY_OK"; then
break
else
keyboard_form
@@ -504,11 +665,11 @@ print_dry_run_files() {
# action so users have a way to actually fix the situation.
not_enough_space() {
local available="${1:-0}"
- step "Not enough free space on $disk"
- say --foreground 1 "$disk has $(to_gb $available) of usable free space; Omarchy needs at least 32GB."
- say "Open the partition tool to free at least 32GB on $disk, then try again."
+ step "$(printf "$L_NOSPACE_STEP" "$disk")"
+ say --foreground 1 "$(printf "$L_NOSPACE_1" "$disk" "$(to_gb $available)")"
+ say "$L_NOSPACE_2"
echo
- if ! gum confirm --affirmative "Back" --negative "Open partition tool" "Return to installation mode?"; then
+ if ! gum confirm --affirmative "$L_BACK" --negative "$L_OPEN_PT" "$L_NOSPACE_CONFIRM"; then
open_partition_tool
fi
return 1
@@ -518,14 +679,14 @@ not_enough_space() {
# encryption confirm. Sets the globals run_partition_execute needs. Returns 1
# to fall back to the install-mode picker.
run_partition_decide() {
- step "Checking for BitLocker on $disk"
+ step "$(printf "$L_BL_STEP" "$disk")"
local bl_part
bl_part=$(detect_bitlocker || true)
if [[ -n "$bl_part" ]]; then
- say --foreground 1 "BitLocker signature detected on $bl_part."
- say "Turn BitLocker off in Windows and wait for the drive to finish decrypting, then try again."
- say "Suspending BitLocker is not enough — the drive stays encrypted."
- abort "Aborted: BitLocker is enabled on this disk."
+ say --foreground 1 "$(printf "$L_BL_1" "$bl_part")"
+ say "$L_BL_2"
+ say "$L_BL_3"
+ abort "$L_BL_ABORT"
fi
# Omarchy always creates its own dedicated ESP in free space and never
@@ -534,17 +695,17 @@ run_partition_decide() {
# Windows ESP is far too small for our Unified Kernel Images, and a shared
# ESP forces the install unencrypted. A separate Linux ESP keeps Windows
# and Omarchy fully isolated and keeps LUKS on the table.
- step "Checking existing EFI partitions on $disk"
+ step "$(printf "$L_EFI_STEP" "$disk")"
local detected_win_esp
detected_win_esp=$(detect_windows_esp || true)
if [[ -n "$detected_win_esp" ]]; then
- say "Found a Windows ESP at $detected_win_esp — leaving it untouched."
- say "Omarchy will create its own dedicated EFI partition in free space."
+ say "$(printf "$L_EFI_WIN" "$detected_win_esp")"
+ say "$L_EFI_OWN"
else
- say "A new EFI partition will be created in free space."
+ say "$L_EFI_NEW"
fi
- step "Analyzing free space on $disk"
+ step "$(printf "$L_FREE_STEP" "$disk")"
partprobe "$disk" 2>/dev/null || true
sleep 1
@@ -619,21 +780,21 @@ run_partition_decide() {
while true; do
clear_logo
echo
- say "Install Omarchy in the $(to_gb $INSTALL_MAX_B) of free space."
+ say "$(printf "$L_FREESAY" "$(to_gb $INSTALL_MAX_B)")"
case $mode in
encrypted)
- say --foreground 8 "Press Ctrl+C for unencrypted install."
- affirmative="Yes, install"
+ say --foreground 8 "$L_CTRLC_UNENC"
+ affirmative="$L_YES_INSTALL"
;;
unencrypted)
- affirmative="Yes, install without encryption"
+ affirmative="$L_YES_NOENC"
;;
esac
echo
- gum confirm --affirmative "$affirmative" --negative "No, change it" \
- "Confirm installing on $disk"
+ gum confirm --affirmative "$affirmative" --negative "$L_NO_CHANGE" \
+ "$(printf "$L_CONFIRM_ON" "$disk")"
confirm_status=$?
case $confirm_status in
@@ -691,9 +852,9 @@ run_partition_execute() {
say "[dry] partition numbers are assigned by parted at creation, then read back"
say "[dry] encrypted=$encrypt_installation kernel=$kernel_choice"
else
- step "Creating partitions on $disk"
+ step "$(printf "$L_MKPART_STEP" "$disk")"
if $needs_mklabel; then
- disk_step "initializing GPT on $disk" parted --script "$disk" mklabel gpt
+ disk_step "$(printf "$L_DS_GPT" "$disk")" parted --script "$disk" mklabel gpt
partprobe "$disk" 2>/dev/null || true
sleep 1
fi
@@ -709,7 +870,7 @@ run_partition_execute() {
disk_abort_hook "Could not create the root partition on $disk"
root_part_num="$created_partition_number"
- disk_step "flagging partition $efi_part_num as ESP" \
+ disk_step "$(printf "$L_DS_ESPFLAG" "$efi_part_num")" \
parted --script "$disk" set "$efi_part_num" esp on
efi_dev=$(partition_path "$disk" "$efi_part_num")
@@ -725,11 +886,11 @@ run_partition_execute() {
# Both partitions are ours and brand new, but the space they occupy may
# carry signatures from whatever was deleted to free it.
- disk_step "clearing stale signatures on $efi_dev" wipefs -af "$efi_dev"
- disk_step "clearing stale signatures on $root_partition_device" wipefs -af "$root_partition_device"
+ disk_step "$(printf "$L_DS_WIPE" "$efi_dev")" wipefs -af "$efi_dev"
+ disk_step "$(printf "$L_DS_WIPE" "$root_partition_device")" wipefs -af "$root_partition_device"
if [[ "$encrypt_installation" == "true" ]]; then
- step "Setting up LUKS2 on $root_partition_device"
+ step "$(printf "$L_LUKS_STEP" "$root_partition_device")"
# Kept as pipes rather than routed through disk_step: the passphrase must
# not become an argv the process table can show. Folding stderr into
# stdout still lands any error in the install log.
@@ -746,31 +907,31 @@ run_partition_execute() {
root_mapper="$root_partition_device"
fi
- step "Creating Btrfs filesystem and subvolumes"
- disk_step "creating the Btrfs filesystem on $root_mapper" \
+ step "$L_BTRFS_STEP"
+ disk_step "$(printf "$L_DS_MKBTRFS" "$root_mapper")" \
mkfs.btrfs -f -L OMARCHY "$root_mapper"
wait_for_device "$root_mapper" || disk_abort_hook "Root device $root_mapper never appeared"
mkdir -p /mnt/btrfs-root
- disk_step "mounting $root_mapper" mount "$root_mapper" /mnt/btrfs-root
+ disk_step "$(printf "$L_DS_MOUNT" "$root_mapper")" mount "$root_mapper" /mnt/btrfs-root
for subvol in @ @home @log @pkg; do
- disk_step "creating subvolume $subvol" btrfs subvolume create "/mnt/btrfs-root/$subvol"
+ disk_step "$(printf "$L_DS_SUBVOL" "$subvol")" btrfs subvolume create "/mnt/btrfs-root/$subvol"
done
umount /mnt/btrfs-root
rmdir /mnt/btrfs-root
- disk_step "mounting the target root" \
+ disk_step "$L_DS_MOUNTROOT" \
mount -o noatime,compress=zstd,subvol=@ "$root_mapper" /mnt
mkdir -p /mnt/home /mnt/var/log /mnt/var/cache/pacman/pkg /mnt"$esp_mount_in_target"
- disk_step "mounting /home" \
+ disk_step "$L_DS_MOUNTHOME" \
mount -o noatime,compress=zstd,subvol=@home "$root_mapper" /mnt/home
- disk_step "mounting /var/log" \
+ disk_step "$L_DS_MOUNTLOG" \
mount -o noatime,compress=zstd,subvol=@log "$root_mapper" /mnt/var/log
- disk_step "mounting the package cache" \
+ disk_step "$L_DS_MOUNTCACHE" \
mount -o noatime,compress=zstd,subvol=@pkg "$root_mapper" /mnt/var/cache/pacman/pkg
- disk_step "creating the ESP filesystem on $efi_dev" mkfs.fat -F32 -n OMARCHY_EFI "$efi_dev"
- disk_step "mounting the ESP" mount "$efi_dev" /mnt"$esp_mount_in_target"
+ disk_step "$(printf "$L_DS_MKESP" "$efi_dev")" mkfs.fat -F32 -n OMARCHY_EFI "$efi_dev"
+ disk_step "$L_DS_MOUNTESP" mount "$efi_dev" /mnt"$esp_mount_in_target"
# The orchestrator's first act is to verify this handoff. Check it here so
# a failure names the step that broke rather than surfacing later as
@@ -798,7 +959,10 @@ run_partition_execute() {
"auth_config": {},
"audio_config": { "audio": "pipewire" },
"bootloader_config": { "bootloader": "Limine", "uki": false, "removable": false },
- "custom_commands": [],
+ "custom_commands": [
+ "sed -i -e s/^#zh_CN.UTF-8/zh_CN.UTF-8/ -e s/^#en_US.UTF-8/en_US.UTF-8/ /etc/locale.gen",
+ "locale-gen"
+ ],
"omarchy_install": {
"mode": "protected",
"target_mount": "/mnt",
@@ -832,7 +996,7 @@ run_partition_execute() {
"locale_config": {
"kb_layout": "$keyboard",
"sys_enc": "UTF-8",
- "sys_lang": "en_US.UTF-8"
+ "sys_lang": "$SYS_LANG"
},
"mirror_config": {
"custom_repositories": [],
@@ -862,7 +1026,7 @@ _EOF_
}
disk_form() {
- step "Let's select where to install Omarchy..."
+ step "$L_DISK_STEP"
# Don't offer the install media as an option (Arch ISO mounts it here)
local boot_source
@@ -888,19 +1052,19 @@ disk_form() {
fi
done <<<"$available_disks"
- selected_display=$(echo "$disk_options" | gum choose --header "Select install disk") || abort
+ selected_display=$(echo "$disk_options" | gum choose --header "$L_DISK_HEADER") || abort
disk=$(echo "$selected_display" | awk '{print $1}')
}
# STEP 4: INSTALL MODE
open_partition_tool() {
- step "Partition tool for $disk"
- gum style "Create unallocated free space for Omarchy, then write changes and quit."
- gum style --foreground 8 "Do not create an Omarchy partition here — leave the target area as Free space."
+ step "$(printf "$L_PT_STEP" "$disk")"
+ gum style "$L_PT_1"
+ gum style --foreground 8 "$L_PT_2"
gum style --foreground 8 "Tip: free-space install needs at least 32GB unallocated, plus 2GB more if no ESP already exists."
echo
- gum confirm --affirmative "Open cfdisk" --negative "Back" "Open partition tool for $disk?" || return 0
+ gum confirm --affirmative "$L_OPEN_CFDISK" --negative "$L_BACK" "$(printf "$L_CFDISK_CONFIRM" "$disk")" || return 0
clear
cfdisk "$disk" || true
@@ -933,17 +1097,17 @@ requires_full_disk_install() {
}
install_mode_form() {
- local choices=("Full disk install")
+ local choices=("$L_MODE_FULL")
# The free-space/protected path registers an EFI boot entry and is UEFI-only.
# Full-disk installs still support BIOS through the orchestrator's BIOS branch.
if [[ -d /sys/firmware/efi ]] && ! $full_disk_only; then
- choices+=("Free space install (alongside existing data)")
+ choices+=("$L_MODE_FREE")
fi
- choices+=("Choose a different disk")
+ choices+=("$L_MODE_OTHER")
- step "Let's select how to install Omarchy..."
- install_mode=$(gum choose --header "Select installation mode on $disk" "${choices[@]}") || abort
+ step "$L_MODE_STEP"
+ install_mode=$(gum choose --header "$(printf "$L_MODE_HEADER" "$disk")" "${choices[@]}") || abort
}
confirm_disk_overwrite() {
@@ -953,17 +1117,17 @@ confirm_disk_overwrite() {
while true; do
clear_logo
echo
- say "Everything will be overwritten. There is no recovery possible."
+ say "$L_WIPE_WARN"
if [[ $mode == "encrypted" ]]; then
- say --foreground 8 "Press Ctrl+C for unencrypted install."
- affirmative="Yes, install"
+ say --foreground 8 "$L_CTRLC_UNENC"
+ affirmative="$L_YES_INSTALL"
else
- affirmative="Yes, install without encryption"
+ affirmative="$L_YES_NOENC"
fi
echo
- gum confirm --affirmative "$affirmative" --negative "No, change it" "Confirm overwriting ${disk}"
+ gum confirm --affirmative "$affirmative" --negative "No, change it" "$(printf "$L_WIPE_CONFIRM" "${disk}")"
confirm_status=$?
case $confirm_status in
@@ -1002,13 +1166,13 @@ select_installation() {
# With no free-space option to offer, a full-disk install is the only
# mode, so skip the picker and go straight to the overwrite confirm.
if $full_disk_only; then
- install_mode="Full disk install"
+ install_mode="$L_MODE_FULL"
else
install_mode_form
fi
case "$install_mode" in
- "Full disk install")
+ "$L_MODE_FULL")
if confirm_disk_overwrite; then
install_target="full_disk"
return 0
@@ -1017,13 +1181,13 @@ select_installation() {
# wants a different disk.
$full_disk_only && disk_form
;;
- "Free space install"*)
+ "$L_MODE_FREE")
if run_partition_decide; then
install_target="free_space"
return 0
fi
;;
- "Choose a different disk")
+ "$L_MODE_OTHER")
disk_form
;;
esac
@@ -1136,7 +1300,10 @@ cat <<-_EOF_ >user_configuration.json
"auth_config": {},
"audio_config": { "audio": "pipewire" },
"bootloader_config": { "bootloader": "Limine", "uki": false, "removable": false },
- "custom_commands": [],
+ "custom_commands": [
+ "sed -i -e s/^#zh_CN.UTF-8/zh_CN.UTF-8/ -e s/^#en_US.UTF-8/en_US.UTF-8/ /etc/locale.gen",
+ "locale-gen"
+ ],
"omarchy_install": {
"mode": "full_disk",
"defer_provisioning": $defer_provisioning,
@@ -1221,7 +1388,7 @@ cat <<-_EOF_ >user_configuration.json
"locale_config": {
"kb_layout": "$keyboard",
"sys_enc": "UTF-8",
- "sys_lang": "en_US.UTF-8"
+ "sys_lang": "$SYS_LANG"
},
"mirror_config": {
"custom_repositories": [],
diff --git a/configs/airootfs/usr/local/bin/omarchy-install-dashboard b/configs/airootfs/usr/local/bin/omarchy-install-dashboard
index 91e960b..9d45f6a 100755
--- a/configs/airootfs/usr/local/bin/omarchy-install-dashboard
+++ b/configs/airootfs/usr/local/bin/omarchy-install-dashboard
@@ -53,6 +53,44 @@ export GUM_CONFIRM_UNSELECTED_BACKGROUND="${GUM_CONFIRM_UNSELECTED_BACKGROUND:-0
child_pid=""
child_pgid=""
+if [[ ${OMARCHY_INSTALL_LANG:-} == zh ]]; then
+tips=(
+ "Super + Space 打开 Omarchy 菜单:应用、设置与更多"
+ "Super + K 查看全部快捷键"
+ "Super 就是键盘上的 Windows / Command 键"
+ "用 Xournal++ 给 PDF 签名"
+ "用 LocalSend 与手机、电脑互传文件"
+ "菜单 Install > Web App 可把任意网站变成应用"
+ "Super + Return 打开终端,Super + Shift + Return 打开浏览器"
+ "图片用 Pinta,视频剪辑用 Kdenlive"
+ "Super + Ctrl + Print 用 OCR 抓取屏幕文字"
+ "Print 截屏,Alt + Print 录屏"
+ "Super + Ctrl + R 设置提醒"
+ "菜单 Style > Theme 一键换主题"
+ "双击顶栏可切换透明"
+ "菜单 Install > Windows 可运行完整 Windows 虚拟机"
+ "Super + Ctrl + V 打开剪贴板管理器"
+ "Super + 1 到 0 切换工作区,加 Shift 带窗口一起"
+ "Super + Print 拾取屏幕任意位置的颜色"
+ "菜单里的 Update 保持系统常新"
+)
+L_INSTALLING="正在安装 Omarchy CN"
+L_TIP="提示:"
+L_DONE_IN="安装完成,用时 %s"
+L_STOPPED="Omarchy 安装已停止"
+L_EXIT_STATUS="安装器退出,状态码 %s"
+L_ENTER="按回车继续……"
+L_REBOOT_NOW="立即重启"
+L_FAIL_HEADER="接下来做什么?"
+L_C_UPLOAD="上传日志求助"
+L_C_VIEWLOG="查看完整日志"
+L_C_SHELL="进入 Shell"
+L_C_REBOOT="重启"
+L_C_POWEROFF="关机"
+L_NO_UPLOADER="当前环境没有可用的日志上传工具。"
+L_LOG_AT="安装日志: %s"
+L_TARGET_LOG="目标系统日志: /mnt/var/log/omarchy-install.log"
+else
tips=(
"Super + Space opens the Omarchy menu for apps, settings, and more"
"Super + K shows all the key bindings"
@@ -61,7 +99,7 @@ tips=(
"Share files with phones and laptops using LocalSend"
"Turn any website into an app with Install > Web App in the menu"
"Super + Return opens a terminal, Super + Shift + Return the browser"
- "Edit images with Pinta, videos with Kdenlive, docs with LibreOffice"
+ "Edit images with Pinta and videos with Kdenlive"
"Super + Ctrl + Print grabs text off the screen with OCR"
"Print takes a screenshot, Alt + Print records the screen"
"Set a reminder with Super + Ctrl + R"
@@ -73,6 +111,23 @@ tips=(
"Super + Print picks a color from anywhere on screen"
"Keep the system fresh with Update in the Omarchy menu"
)
+L_INSTALLING="Installing Omarchy"
+L_TIP="Tip:"
+L_DONE_IN="Installed Omarchy in %s"
+L_STOPPED="Omarchy installation stopped"
+L_EXIT_STATUS="Installer exited with status %s"
+L_ENTER="Press Enter to continue…"
+L_REBOOT_NOW="Reboot Now"
+L_FAIL_HEADER="What would you like to do?"
+L_C_UPLOAD="Upload log for support"
+L_C_VIEWLOG="View full log"
+L_C_SHELL="Drop to shell"
+L_C_REBOOT="Reboot"
+L_C_POWEROFF="Power off"
+L_NO_UPLOADER="No log uploader is available in this environment."
+L_LOG_AT="Install log: %s"
+L_TARGET_LOG="Target log: /mnt/var/log/omarchy-install.log"
+fi
cleanup() {
printf '%s%s' "$RESET" "$SHOW_CURSOR" >"$TTY_PATH" 2>/dev/null || true
@@ -452,13 +507,13 @@ render_dynamic() {
LAST_PM=$pm
printf '%s%d;1H' "$CSI" "$DYNAMIC_ROW"
- center "Installing Omarchy" "$CONTENT_WIDTH"
+ center "$L_INSTALLING" "$CONTENT_WIDTH"
blank_line
line_at "$CONTENT_WIDTH" $(( (CONTENT_WIDTH - 34) / 2 )) ""
progress_bar "$pm" 34
printf '\n'
blank_line
- center "${DIM}Tip:${RESET} ${GREEN}$(current_tip)${RESET}" "$CONTENT_WIDTH"
+ center "${DIM}${L_TIP}${RESET} ${GREEN}$(current_tip)${RESET}" "$CONTENT_WIDTH"
printf '%s' "$CLEAR_TO_END"
}
@@ -511,7 +566,7 @@ render_finish() {
printf '%s%s%s%d;1H' "$SHOW_CURSOR" "$CLEAR" "$CSI" "$FINISH_TOP_ROW"
render_logo
blank_line
- center "Installed Omarchy in ${duration}" "$CONTENT_WIDTH"
+ center "$(printf "$L_DONE_IN" "${duration}")" "$CONTENT_WIDTH"
blank_line
} >"$TTY_PATH"
@@ -556,7 +611,7 @@ reboot_prompt() {
--padding "0 0 0 $prompt_pad" \
--show-help=false \
--default \
- --affirmative "Reboot Now" \
+ --affirmative "$L_REBOOT_NOW" \
--negative "" \
"" <"$TTY_PATH" >"$TTY_PATH" 2>&1
}
@@ -617,7 +672,7 @@ find_log_uploader() {
}
prompt_enter() {
- printf '\nPress Enter to continue…' >"$TTY_PATH"
+ printf '\n%s' "$L_ENTER" >"$TTY_PATH"
IFS= read -r _ <"$TTY_PATH" || true
}
@@ -628,9 +683,9 @@ upload_failure_log() {
"$uploader" install >"$TTY_PATH" 2>&1 || true
else
{
- echo "No log uploader is available in this environment."
- echo "Install log: $LOG_FILE"
- [[ -f /mnt/var/log/omarchy-install.log ]] && echo "Target log: /mnt/var/log/omarchy-install.log"
+ echo "$L_NO_UPLOADER"
+ printf "$L_LOG_AT\n" "$LOG_FILE"
+ [[ -f /mnt/var/log/omarchy-install.log ]] && echo "$L_TARGET_LOG"
} >"$TTY_PATH"
fi
prompt_enter
@@ -696,8 +751,8 @@ render_failure() {
blank_line
render_logo
blank_line
- center "${RED}Omarchy installation stopped${RESET}" "$CONTENT_WIDTH"
- center "Installer exited with status $status" "$CONTENT_WIDTH"
+ center "${RED}${L_STOPPED}${RESET}" "$CONTENT_WIDTH"
+ center "$(printf "$L_EXIT_STATUS" "$status")" "$CONTENT_WIDTH"
render_media_diagnosis "$media_diagnosis"
@@ -730,40 +785,40 @@ failure_menu() {
if [[ -n $uploader ]]; then
choice=$(gum choose \
--height 6 \
- --header "What would you like to do?" \
- "Upload log for support" \
- "View full log" \
- "Drop to shell" \
- "Reboot" \
- "Power off" \
- <"$TTY_PATH" 2>"$TTY_PATH") || choice="Drop to shell"
+ --header "$L_FAIL_HEADER" \
+ "$L_C_UPLOAD" \
+ "$L_C_VIEWLOG" \
+ "$L_C_SHELL" \
+ "$L_C_REBOOT" \
+ "$L_C_POWEROFF" \
+ <"$TTY_PATH" 2>"$TTY_PATH") || choice="$L_C_SHELL"
else
choice=$(gum choose \
--height 5 \
- --header "What would you like to do?" \
- "View full log" \
- "Drop to shell" \
- "Reboot" \
- "Power off" \
- <"$TTY_PATH" 2>"$TTY_PATH") || choice="Drop to shell"
+ --header "$L_FAIL_HEADER" \
+ "$L_C_VIEWLOG" \
+ "$L_C_SHELL" \
+ "$L_C_REBOOT" \
+ "$L_C_POWEROFF" \
+ <"$TTY_PATH" 2>"$TTY_PATH") || choice="$L_C_SHELL"
fi
case "$choice" in
- "Upload log for support")
+ "$L_C_UPLOAD")
upload_failure_log
render_failure "${failure_status:-1}" "${failure_summary:-}" "${failure_media_diagnosis:-}" >"$TTY_PATH" 2>/dev/null || true
;;
- "View full log")
+ "$L_C_VIEWLOG")
view_failure_log
render_failure "${failure_status:-1}" "${failure_summary:-}" "${failure_media_diagnosis:-}" >"$TTY_PATH" 2>/dev/null || true
;;
- "Reboot")
+ "$L_C_REBOOT")
reboot 2>/dev/null || systemctl reboot 2>/dev/null || true
;;
- "Power off")
+ "$L_C_POWEROFF")
poweroff 2>/dev/null || systemctl poweroff 2>/dev/null || true
;;
- "Drop to shell"|"")
+ "$L_C_SHELL"|"")
return 0
;;
esac
@@ -1,32 +0,0 @@
#!/bin/bash
set -euo pipefail
source "$(dirname "${BASH_SOURCE[0]}")/base-test.sh"
require_command node
# A keyless unpacked extension gets a path-derived id, so its settings — and
# any shortcut registrations it may grow — would go stale if its load path or
# packaging ever changed. The pinned key keeps the id stable everywhere.
whatsapp_slim_id=$(node - <<'JS' "$ROOT/default/chromium/extensions/whatsapp-slim/manifest.json"
const crypto = require('crypto')
const fs = require('fs')
const manifest = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'))
const hash = crypto.createHash('sha256').update(Buffer.from(manifest.key, 'base64')).digest()
const alphabet = 'abcdefghijklmnop'
let id = ''
for (const byte of hash.subarray(0, 16)) {
id += alphabet[byte >> 4]
id += alphabet[byte & 0x0f]
}
process.stdout.write(id)
JS
)
[[ $whatsapp_slim_id == "amhpgjbcfakkmkeojmoaoiochhifohdi" ]] ||
fail "whatsapp-slim extension manifest has the stable id" "$whatsapp_slim_id"
pass "whatsapp-slim extension manifest has the stable id"
+1 -1
View File
@@ -265,7 +265,7 @@ shell_json=$(cd "$ROOT" && jq -r '[.bar.layout.center[].id] | join(",")' config/
[[ $shell_json != *"omarchy.calendar"* ]] || fail "clock hosts the calendar instead of a second bar pill" "center: $shell_json"
pass "default bar layout includes the clock widget"
grep -q 'o.bind("SUPER + CTRL + ALT + D", "Calendar", "omarchy-shell shell toggle omarchy.clock")' \
grep -q 'o.bind("SUPER + CTRL + ALT + D", "日历", "omarchy-shell shell toggle omarchy.clock")' \
"$ROOT/default/hypr/bindings/utilities.lua" ||
fail "SUPER+CTRL+ALT+D toggles the calendar panel"
pass "SUPER+CTRL+ALT+D toggles the calendar panel"
+18 -3
View File
@@ -39,16 +39,30 @@ cat >"$stub_bin/gpasswd" <<'STUB'
#!/bin/bash
echo "$@" >>"${GPASSWD_CALLS:?}"
STUB
chmod +x "$stub_bin/id" "$stub_bin/sudo" "$stub_bin/gpasswd"
# gum confirm always says yes, and reboot records that it fired: the migration
# must still NOT reboot (it defers to omarchy-update-restart), so neither should
# be reached.
cat >"$stub_bin/gum" <<'STUB'
#!/bin/bash
[[ $1 == confirm ]] && exit 0
exit 0
STUB
cat >"$stub_bin/omarchy-system-reboot" <<'STUB'
#!/bin/bash
touch "${REBOOT_CALLED:?}"
STUB
chmod +x "$stub_bin/id" "$stub_bin/sudo" "$stub_bin/gpasswd" "$stub_bin/gum" "$stub_bin/omarchy-system-reboot"
reboot_flag="$home/.local/state/omarchy/reboot-required"
gpasswd_calls="$test_dir/gpasswd-calls"
reboot_called="$test_dir/reboot-called"
launcher="$home/.local/share/applications/Docker.desktop"
run_migration() {
rm -f "$gpasswd_calls" "$reboot_flag"
rm -f "$gpasswd_calls" "$reboot_flag" "$reboot_called"
HOME="$home" OMARCHY_PATH="$omarchy_path" USER="tester" STUB_GROUPS="$1" \
GPASSWD_CALLS="$gpasswd_calls" PATH="$stub_bin:$ROOT/bin:$PATH" \
GPASSWD_CALLS="$gpasswd_calls" REBOOT_CALLED="$reboot_called" \
PATH="$stub_bin:$ROOT/bin:$PATH" \
bash -euo pipefail "$migration" >/dev/null 2>&1
}
@@ -56,6 +70,7 @@ run_migration() {
run_migration "wheel input docker" || fail "migration runs when the user is in the docker group"
grep -q -- "-d tester docker" "$gpasswd_calls" || fail "migration removes the user from the docker group"
[[ -f $reboot_flag ]] || fail "migration flags a reboot so the group change takes effect"
[[ ! -f $reboot_called ]] || fail "migration must defer the reboot (not reboot mid-update)"
[[ $(cat "$launcher") == "NEW-LAUNCHER" ]] || fail "migration refreshes the stale Docker launcher entry"
pass "migration removes the group, flags a reboot, and refreshes the launcher"
@@ -146,9 +146,9 @@ chmod +x "$stub_bin/voxtype"
bindings=$(PATH="$stub_bin:$PATH" list_bindings "$home")
[[ -n $bindings ]] || fail "default bindings load for the conflict check"
grep -Fq $'SUPER + RETURN\tTerminal' <<<"$bindings" || fail "conflict check sees the essential bindings"
grep -Fq $'SUPER + RETURN\t终端' <<<"$bindings" || fail "conflict check sees the essential bindings"
grep -Fq $'SUPER + SHIFT + A\tChatGPT' <<<"$bindings" || fail "conflict check sees the preinstalled bindings"
grep -Fq $'F9\tStart dictation (push-to-talk)' <<<"$bindings" || fail "conflict check sees the Voxtype bindings"
grep -Fq $'F9\t开始听写(按住说话)' <<<"$bindings" || fail "conflict check sees the Voxtype bindings"
pass "conflict check covers the full default binding set"
duplicates=$(duplicate_signatures <<<"$bindings")
+13 -13
View File
@@ -101,7 +101,7 @@ trap 'rm -rf "$tmpdir"' EXIT
fresh_home="$tmpdir/fresh-home"
mkdir -p "$fresh_home"
fresh_output=$(run_application_bindings "$fresh_home")
grep -Fq $'SUPER + RETURN Terminal' <<<"$fresh_output" || fail "default application bindings include essentials"
grep -Fq $'SUPER + RETURN 终端' <<<"$fresh_output" || fail "default application bindings include essentials"
grep -Fq $'SUPER + SHIFT + A ChatGPT' <<<"$fresh_output" || fail "default application bindings include preinstalled web apps"
pass "default application bindings load from package defaults"
@@ -123,7 +123,7 @@ removed_home="$tmpdir/removed-home"
mkdir -p "$removed_home/.local/state/omarchy"
touch "$removed_home/.local/state/omarchy/preinstalls-removed"
removed_output=$(run_application_bindings "$removed_home")
grep -Fq $'SUPER + RETURN Terminal' <<<"$removed_output" || fail "preinstall removal keeps essential bindings"
grep -Fq $'SUPER + RETURN 终端' <<<"$removed_output" || fail "preinstall removal keeps essential bindings"
if grep -Fq $'SUPER + SHIFT + A ChatGPT' <<<"$removed_output"; then
fail "preinstall removal skips preinstalled web app bindings"
fi
@@ -132,7 +132,7 @@ pass "preinstall removal flag skips optional application bindings"
variable_home="$tmpdir/variable-home"
mkdir -p "$variable_home"
variable_output=$(run_application_bindings "$variable_home" 'omarchy_preinstalled_bindings = false')
grep -Fq $'SUPER + RETURN Terminal' <<<"$variable_output" || fail "preinstalled binding variable keeps essential bindings"
grep -Fq $'SUPER + RETURN 终端' <<<"$variable_output" || fail "preinstalled binding variable keeps essential bindings"
if grep -Fq $'SUPER + SHIFT + A ChatGPT' <<<"$variable_output"; then
fail "preinstalled binding variable skips optional application bindings"
fi
@@ -150,17 +150,17 @@ mkdir -p "$voxtype_home" "$voxtype_bin"
touch "$voxtype_bin/voxtype"
chmod +x "$voxtype_bin/voxtype"
voxtype_output=$(PATH="$voxtype_bin:$PATH" run_omarchy_bindings "$voxtype_home")
grep -Fq $'SUPER + CTRL + X Toggle dictation' <<<"$voxtype_output" ||
grep -Fq $'SUPER + CTRL + X 切换语音听写' <<<"$voxtype_output" ||
fail "installed Voxtype enables its toggle binding"
grep -Fq $'F9 Start dictation (push-to-talk)' <<<"$voxtype_output" ||
grep -Fq $'F9 开始听写(按住说话)' <<<"$voxtype_output" ||
fail "installed Voxtype enables its push-to-talk binding"
grep -Fq $'F9 Stop dictation (push-to-talk)' <<<"$voxtype_output" ||
grep -Fq $'F9 停止听写(按住说话)' <<<"$voxtype_output" ||
fail "installed Voxtype enables its release binding"
pass "installed Voxtype conditionally enables dictation bindings"
voxtype_without_execute_output=$(PATH="$voxtype_bin:$PATH" run_omarchy_bindings \
"$voxtype_home" 'os.execute = function() return nil, "No child processes", 10 end')
grep -Fq $'SUPER + CTRL + X Toggle dictation' <<<"$voxtype_without_execute_output" ||
grep -Fq $'SUPER + CTRL + X 切换语音听写' <<<"$voxtype_without_execute_output" ||
fail "Voxtype detection does not require spawning a subprocess"
pass "installed Voxtype detection works without os.execute"
@@ -170,7 +170,7 @@ ln -s "$(command -v lua)" "$missing_bin/lua"
ln -s "$(command -v lspci)" "$missing_bin/lspci"
ln -s "$(command -v sort)" "$missing_bin/sort"
missing_voxtype_output=$(PATH="$missing_bin" run_omarchy_bindings "$voxtype_home")
if grep -Fq $'SUPER + CTRL + X Toggle dictation' <<<"$missing_voxtype_output"; then
if grep -Fq $'SUPER + CTRL + X 切换语音听写' <<<"$missing_voxtype_output"; then
fail "missing Voxtype skips its bindings"
fi
pass "missing Voxtype skips dictation bindings"
@@ -180,13 +180,13 @@ pass "missing Voxtype skips dictation bindings"
scratchpad_home="$tmpdir/scratchpad-home"
mkdir -p "$scratchpad_home"
scratchpad_output=$(run_omarchy_bindings "$scratchpad_home")
grep -Fqx $'SUPER + S Toggle scratchpad' <<<"$scratchpad_output" ||
grep -Fqx $'SUPER + S 切换便签区' <<<"$scratchpad_output" ||
fail "scratchpad keeps its existing toggle binding"
grep -Fqx $'SUPER + grave Toggle scratchpad' <<<"$scratchpad_output" ||
grep -Fqx $'SUPER + grave 切换便签区' <<<"$scratchpad_output" ||
fail "scratchpad supports a Quake-style toggle binding"
grep -Fqx $'SUPER + ALT + S Move window to scratchpad' <<<"$scratchpad_output" ||
grep -Fqx $'SUPER + ALT + S 窗口移入便签区' <<<"$scratchpad_output" ||
fail "scratchpad keeps its existing move binding"
grep -Fqx $'SUPER + SHIFT + grave Move window to scratchpad' <<<"$scratchpad_output" ||
grep -Fqx $'SUPER + SHIFT + grave 窗口移入便签区' <<<"$scratchpad_output" ||
fail "scratchpad supports a Quake-style move binding"
pass "scratchpad retains existing bindings and adds Grave shortcuts"
@@ -197,7 +197,7 @@ panels_home="$tmpdir/panels-home"
mkdir -p "$panels_home"
panels_output=$(run_omarchy_bindings "$panels_home")
for panel in 1 2 3 4 5 6 7 8 9; do
grep -Fqx "SUPER + CTRL + code:$((panel + 9))"$'\t'"Bar panel $panel" <<<"$panels_output" ||
grep -Fqx "SUPER + CTRL + code:$((panel + 9))"$'\t'"顶栏面板 $panel" <<<"$panels_output" ||
fail "bar panel hotkeys count the right section" "$panel"
done
number_claims=$(cut -f1 <<<"$panels_output" | grep -cE '^SUPER \+ CTRL \+ code:1[0-9]$' || true)
+38 -38
View File
@@ -50,24 +50,24 @@ keybindings() {
# binds twice on purpose. The last bind carries the longest description Omarchy
# ships, which is what puts a row closest to the width the menu allows.
stub_hyprctl <<BINDS
$(lua_bind 64 "SUPER + W" "Close window")
$(lua_bind 64 "SUPER + Q" "Close window")
$(lua_bind 64 "SUPER + F" "Full screen")
$(lua_bind 64 "SUPER + S" "Toggle scratchpad")
$(lua_bind 64 "SUPER + grave" "Toggle scratchpad")
$(exec_bind 73 "SUPER SHIFT ALT + 0" "Move window silently to workspace 10" "true")
$(lua_bind 64 "SUPER + W" "关闭窗口")
$(lua_bind 64 "SUPER + Q" "关闭窗口")
$(lua_bind 64 "SUPER + F" "全屏")
$(lua_bind 64 "SUPER + S" "切换便签区")
$(lua_bind 64 "SUPER + grave" "切换便签区")
$(exec_bind 73 "SUPER SHIFT ALT + 0" "窗口静默移到工作区 10" "true")
BINDS
rendered=$(keybindings)
[[ -n $rendered ]] || fail "the keybindings menu renders with a stubbed Hyprland"
grep -q 'SUPER + F *→ Full screen' <<<"$rendered" ||
grep -q 'SUPER + F *→ 全屏' <<<"$rendered" ||
fail "a chord with no alternative renders on its own" "$rendered"
pass "the keybindings menu renders its entries"
(( $(grep -c '→ Close window$' <<<"$rendered") == 1 )) ||
(( $(grep -c '→ 关闭窗口$' <<<"$rendered") == 1 )) ||
fail "an alternative chord joins the row of the first one" "$rendered"
grep -q 'SUPER + W / SUPER + Q *→ Close window' <<<"$rendered" ||
grep -q 'SUPER + W / SUPER + Q *→ 关闭窗口' <<<"$rendered" ||
fail "a shared row names both chords" "$rendered"
pass "an alternative chord joins the row of the first one"
@@ -78,7 +78,7 @@ grep -q '^SUPER + W / SUPER + Q' <<<"$rendered" ||
pass "the chord declared first leads a shared row"
# Hyprland calls the key left of 1 "grave". Nobody reads their keyboard that way.
grep -q 'SUPER + S / SUPER + ~ *→ Toggle scratchpad' <<<"$rendered" ||
grep -q 'SUPER + S / SUPER + ~ *→ 切换便签区' <<<"$rendered" ||
fail "the grave key reads as the symbol printed on it" "$rendered"
! grep -q 'grave' <<<"$rendered" ||
fail "no entry still says grave" "$rendered"
@@ -101,14 +101,14 @@ pass "no entry outgrows the width the menu gives it"
# the entry: XF86Calculator alone belongs in the tail the menu keeps for media
# keys, while the calculator itself sits in the body of the list.
stub_hyprctl <<BINDS
$(exec_bind 68 "SUPER CTRL + Q" "Calculator" "omacalc")
$(exec_bind 0 "XF86Calculator" "Calculator" "omacalc")
$(exec_bind 8 "ALT + TAB" "Reveal active window on top" "true")
$(exec_bind 68 "SUPER CTRL + Q" "计算器" "omacalc")
$(exec_bind 0 "XF86Calculator" "计算器" "omacalc")
$(exec_bind 8 "ALT + TAB" "活动窗口置顶显示" "true")
BINDS
rendered=$(keybindings)
(( $(grep -n '→ Calculator$' <<<"$rendered" | cut -d: -f1) <
$(grep -n '→ Reveal active window on top$' <<<"$rendered" | cut -d: -f1) )) ||
(( $(grep -n '→ 计算器$' <<<"$rendered" | cut -d: -f1) <
$(grep -n '→ 活动窗口置顶显示$' <<<"$rendered" | cut -d: -f1) )) ||
fail "a shared chord does not change where its entry ranks" "$rendered"
pass "a shared chord does not change where its entry ranks"
@@ -121,51 +121,51 @@ bind
key:
keycode: 49
catchall: false
description: Toggle scratchpad
description: 切换便签区
dispatcher: exec
arg: true
BINDS
rendered=$(keybindings)
grep -q 'SUPER + ~ *→ Toggle scratchpad' <<<"$rendered" ||
grep -q 'SUPER + ~ *→ 切换便签区' <<<"$rendered" ||
fail "a keycode resolves to the symbol printed on the key too" "$rendered"
pass "a keycode resolves to the symbol printed on the key too"
# A chord refused for width opens a row of its own, and the next chord tries
# that row rather than reaching back past it and printing out of order.
stub_hyprctl <<BINDS
$(exec_bind 64 "SUPER + A" "Calculator" "omacalc")
$(exec_bind 77 "SUPER SHIFT CTRL ALT + BACKSPACE" "Calculator" "omacalc")
$(exec_bind 64 "SUPER + B" "Calculator" "omacalc")
$(exec_bind 64 "SUPER + A" "计算器" "omacalc")
$(exec_bind 77 "SUPER SHIFT CTRL ALT + BACKSPACE" "计算器" "omacalc")
$(exec_bind 64 "SUPER + B" "计算器" "omacalc")
BINDS
rendered=$(keybindings)
(( $(grep -c '→ Calculator$' <<<"$rendered") == 3 )) ||
(( $(grep -c '→ 计算器$' <<<"$rendered") == 3 )) ||
fail "a refused chord does not let the next one jump the queue" "$rendered"
pass "a refused chord does not let the next one jump the queue"
# Sharing a row is something Omarchy names an action for, not something two
# chords earn by looking alike. Alt + Tab and Shift + Alt + Tab both read
# "Reveal active window on top" and cycle opposite ways.
# "活动窗口置顶显示" and cycle opposite ways.
stub_hyprctl <<BINDS
$(exec_bind 64 "SUPER + Y" "Zoom in" "omarchy-zoom in")
$(exec_bind 64 "SUPER + Z" "Zoom in" "omarchy-zoom in")
$(exec_bind 64 "SUPER + Y" "放大" "omarchy-zoom in")
$(exec_bind 64 "SUPER + Z" "放大" "omarchy-zoom in")
BINDS
rendered=$(keybindings)
(( $(grep -c '→ Zoom in$' <<<"$rendered") == 2 )) ||
(( $(grep -c '→ 放大$' <<<"$rendered") == 2 )) ||
fail "an action Omarchy did not name keeps its chords on separate rows" "$rendered"
pass "an action Omarchy did not name keeps its chords on separate rows"
# Even a named action gives up the shared row rather than overrun the column:
# two rows in line beat one that juts out of it.
stub_hyprctl <<BINDS
$(exec_bind 73 "SUPER SHIFT ALT + BACKSPACE" "Calculator" "omacalc")
$(exec_bind 69 "SUPER SHIFT CTRL + BACKSPACE" "Calculator" "omacalc")
$(exec_bind 73 "SUPER SHIFT ALT + BACKSPACE" "计算器" "omacalc")
$(exec_bind 69 "SUPER SHIFT CTRL + BACKSPACE" "计算器" "omacalc")
BINDS
rendered=$(keybindings)
(( $(grep -c '→ Calculator$' <<<"$rendered") == 2 )) ||
(( $(grep -c '→ 计算器$' <<<"$rendered") == 2 )) ||
fail "chords too wide to share a row stay on their own" "$rendered"
[[ $(awk -F '→' '{ print length($1) }' <<<"$rendered" | sort -u) == "36" ]] ||
fail "chords too wide to share a row leave the column alone" "$rendered"
@@ -174,24 +174,24 @@ pass "chords too wide to share a row stay on their own"
# A shared label is not a shared action. Two chords that merely read alike have
# to stay apart, or the menu hides one of them behind the other.
stub_hyprctl <<BINDS
$(lua_bind 64 "SUPER + W" "Close window")
$(exec_bind 64 "SUPER + X" "Close window" "omarchy-hyprland-window-close-all")
$(lua_bind 64 "SUPER + W" "关闭窗口")
$(exec_bind 64 "SUPER + X" "关闭窗口" "omarchy-hyprland-window-close-all")
BINDS
rendered=$(keybindings)
(( $(grep -c '→ Close window$' <<<"$rendered") == 2 )) ||
(( $(grep -c '→ 关闭窗口$' <<<"$rendered") == 2 )) ||
fail "chords with the same label but different actions stay apart" "$rendered"
pass "chords with the same label but different actions stay apart"
# An unresolved Lua bind reports no dispatcher at all, so nothing says the two
# chords run the same thing, whatever their label promises.
stub_hyprctl <<BINDS
$(lua_bind 64 "SUPER + Y" "Close window")
$(lua_bind 64 "SUPER + Z" "Close window")
$(lua_bind 64 "SUPER + Y" "关闭窗口")
$(lua_bind 64 "SUPER + Z" "关闭窗口")
BINDS
rendered=$(keybindings)
(( $(grep -c '→ Close window$' <<<"$rendered") == 2 )) ||
(( $(grep -c '→ 关闭窗口$' <<<"$rendered") == 2 )) ||
fail "chords whose dispatch is unknown stay apart" "$rendered"
pass "chords whose dispatch is unknown stay apart"
@@ -199,10 +199,10 @@ pass "chords whose dispatch is unknown stay apart"
# the script, so dropping an action from the list fails instead of shrinking
# what gets checked.
expected_alternatives=(
"Close window"
"Calculator"
"Toggle scratchpad"
"Move window to scratchpad"
"关闭窗口"
"计算器"
"切换便签区"
"窗口移入便签区"
)
eval "$(sed -n '/^alternative_chord_actions()/,/^}/p' "$ROOT/bin/omarchy-menu-keybindings")"
+7 -3
View File
@@ -152,9 +152,13 @@ assert(
'menu ranks an app matching the query as a whole word above exact-labeled menu entries'
)
assert(
rankScore('style.font', 'font') < rankScore('apps.fontforge', 'font'),
rankScore('style.font', '字体') < rankScore('apps.fontforge', '字体'),
'menu keeps a better-matching menu entry above a weaker app match'
)
assert(
rankScore('style.font', 'font') < rankScore('style.background', 'font'),
'menu still reaches localized entries through their English leaf id'
)
// Routing: htop ships `Keywords=system;...`, which app rows carry as aliases.
// An installed app must never capture a menu route (SUPER+ESCAPE opens the
@@ -244,7 +248,7 @@ assertDeepEqual(
defaultItems
.filter(item => item.parent === 'setup.default.agent')
.map(item => item.label),
['Antigravity', 'Claude', 'Codex', 'Copilot', 'Crush', 'Grok', 'omp', 'OpenCode', 'Ori', 'Pi'],
['Antigravity', 'Claude', 'Codex', 'Copilot', 'Crush', 'Deep Code', 'Dim Agent', 'Grok', 'Kimi Code', 'omp', 'OpenCode', 'Ori', 'Pi', '国产模型组合 (AI Hub)'],
'menu sorts coding agents alphabetically'
)
const expectedDefaults = {
@@ -338,7 +342,7 @@ assertEqual(
)
assertDeepEqual(
defaultItems.filter(item => item.parent === 'setup.plugin').map(item => item.label),
['Enable Plugin', 'Disable Plugin', 'Add Plugin', 'Clone Plugin', 'Remove Plugin'],
['启用插件', '停用插件', '添加插件', '克隆插件', '移除插件'],
'menu manages plugins from Setup > Plugins'
)
assert(
+5 -2
View File
@@ -34,7 +34,7 @@ export PATH="$TEST_HOME/stubs:$PATH"
for reg in ai-providers ai-harnesses ai-compatibility; do
jq -e . "$ROOT/cn/registry/$reg.json" > /dev/null || fail "$reg.json parses"
done
for p in deepseek kimi zai; do
for p in deepseek kimi zai minimax; do
jq -e --arg p "$p" '.providers[$p].endpoints.anthropic // .providers[$p].endpoints.openai' \
"$ROOT/cn/registry/ai-providers.json" > /dev/null || fail "provider $p has an endpoint"
done
@@ -67,13 +67,16 @@ declare -A bases=(
[deepseek]="https://api.deepseek.com/anthropic"
[kimi]="https://api.kimi.com/coding/"
[zai]="https://api.z.ai/api/anthropic"
[minimax]="https://api.minimaxi.com/anthropic"
)
for p in deepseek kimi zai; do
for p in deepseek kimi zai minimax; do
out=$(cn_ai_render_env claude-code "$p" test-model sk-x)
grep -qF "ANTHROPIC_BASE_URL=${bases[$p]}" <<<"$out" || fail "claude-code $p base url" "$out"
grep -q "ANTHROPIC_AUTH_TOKEN=sk-x" <<<"$out" || fail "claude-code $p auth token"
grep -q "ANTHROPIC_API_KEY=sk-x" <<<"$out" || fail "claude-code $p api key"
done
out=$(cn_ai_render_env claude-code minimax "MiniMax-M3[1m]" sk-x)
grep -qF "ANTHROPIC_DEFAULT_HAIKU_MODEL=MiniMax-M3" <<<"$out" || fail "minimax haiku slot falls back to default-coding"
out=$(cn_ai_render_env opencode deepseek test-model sk-x)
grep -q "DEEPSEEK_API_KEY=sk-x" <<<"$out" || fail "opencode deepseek env"
if cn_ai_render_env opencode zai m k > /dev/null 2>&1; then
+2 -2
View File
@@ -282,9 +282,9 @@ pass "webcam sizes stay distinct and inside a narrow region"
rm -f "$region_file"
grep -F 'o.bind("SUPER + ALT + code:34", "Make webcam overlay smaller", "omarchy-capture-webcam-resize smaller")' \
grep -F 'o.bind("SUPER + ALT + code:34", "缩小摄像头悬浮窗", "omarchy-capture-webcam-resize smaller")' \
"$ROOT/default/hypr/bindings/utilities.lua" >/dev/null || fail "webcam smaller hotkey is configured"
grep -F 'o.bind("SUPER + ALT + code:35", "Make webcam overlay larger", "omarchy-capture-webcam-resize larger")' \
grep -F 'o.bind("SUPER + ALT + code:35", "放大摄像头悬浮窗", "omarchy-capture-webcam-resize larger")' \
"$ROOT/default/hypr/bindings/utilities.lua" >/dev/null || fail "webcam larger hotkey is configured"
pass "webcam resize hotkeys are configured"
+67
View File
@@ -0,0 +1,67 @@
#!/bin/bash
#
# omarchy-sudo-docker is the single answer to "does Docker need sudo", and it
# answers two different questions on purpose. The default asks whether this
# session can reach the socket, which is what decides if a command must elevate.
# --configured asks whether the account is set up for sudoless Docker, which is
# what the menu needs so it offers the toggle that can change state. Between
# enabling sudoless Docker and the reboot that grants the group, those disagree.
set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
command="$ROOT/bin/omarchy-sudo-docker"
# Stub id so the configured groups are controllable.
mkdir -p "$TMPDIR/bin"
cat >"$TMPDIR/bin/id" <<'STUB'
#!/bin/bash
printf '%s\n' "${STUB_GROUPS:-wheel input}"
STUB
chmod +x "$TMPDIR/bin/id"
# A writable stand-in means the socket is reachable; an unwritable one means it
# is not. Test the file mode rather than a live daemon.
reachable_socket="$TMPDIR/reachable.sock"
blocked_socket="$TMPDIR/blocked.sock"
touch "$reachable_socket" "$blocked_socket"
chmod 600 "$reachable_socket"
chmod 400 "$blocked_socket"
run() { # SOCKET GROUPS [--configured]
env PATH="$TMPDIR/bin:$PATH" OMARCHY_DOCKER_SOCKET="$1" STUB_GROUPS="$2" USER=tester \
bash "$command" ${3:+"$3"}
}
# Default mode follows the socket, not the group list.
run "$blocked_socket" "wheel input" || fail "an unreachable socket means Docker needs sudo"
run "$reachable_socket" "wheel input" && fail "a reachable socket means Docker does not need sudo"
pass "default mode answers from the socket this session can reach"
# A socket that isn't there at all still needs elevation (starting it is root work).
run "$TMPDIR/absent.sock" "wheel input docker" || fail "a missing socket means Docker needs sudo"
pass "a missing socket counts as needing sudo"
# --configured follows the account's groups, not the socket.
run "$blocked_socket" "wheel input docker" --configured && fail "a configured docker group means no sudo is needed"
run "$reachable_socket" "wheel input" --configured || fail "no docker group means sudo is needed"
pass "--configured answers from the account's groups"
# The window this split exists for: sudoless Docker has just been enabled, so the
# account carries the group while the running session still cannot use it. The
# menu must offer Remove (--configured says no sudo) while lazydocker and the
# Windows VM must still prompt (default says sudo).
run "$blocked_socket" "wheel input docker" || fail "the session still needs sudo before the reboot"
run "$blocked_socket" "wheel input docker" --configured && fail "the account is already configured for sudoless Docker"
pass "the two modes disagree between enabling sudoless Docker and the reboot"
# An unknown argument is a usage error, not a silent answer either way.
run "$reachable_socket" "wheel input" --bogus 2>/dev/null && fail "an unknown flag exits non-zero"
status=0
run "$reachable_socket" "wheel input" --bogus >/dev/null 2>&1 || status=$?
(( status == 2 )) || fail "an unknown flag exits 2, not the boolean 1"
pass "an unknown flag is a usage error"
@@ -0,0 +1,93 @@
#!/bin/bash
#
# Toggling sudoless Docker changes the docker group, which only takes effect on a
# reboot. The setup/remove commands must flag the reboot and offer to do it now
# (gum confirm), but defer it when OMARCHY_DEFER_REBOOT is set (the migration
# reuses them inside `omarchy update`, where omarchy-update-restart handles it).
set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
test_dir=$(mktemp -d)
trap 'rm -rf "$test_dir"' EXIT
home="$test_dir/home"
stub_bin="$test_dir/bin"
mkdir -p "$home" "$stub_bin"
cat >"$stub_bin/id" <<'STUB'
#!/bin/bash
printf '%s\n' "${STUB_GROUPS:-wheel input}"
STUB
cat >"$stub_bin/sudo" <<'STUB'
#!/bin/bash
exec "$@"
STUB
cat >"$stub_bin/usermod" <<'STUB'
#!/bin/bash
echo "$@" >>"${USERMOD_CALLS:?}"
STUB
cat >"$stub_bin/gpasswd" <<'STUB'
#!/bin/bash
echo "$@" >>"${GPASSWD_CALLS:?}"
STUB
cat >"$stub_bin/gum" <<'STUB'
#!/bin/bash
touch "${GUM_CALLED:?}"
exit "${GUM_ANSWER:-0}"
STUB
cat >"$stub_bin/omarchy-system-reboot" <<'STUB'
#!/bin/bash
touch "${REBOOT_CALLED:?}"
STUB
chmod +x "$stub_bin"/*
reboot_flag="$home/.local/state/omarchy/reboot-required"
gum_called="$test_dir/gum-called"
reboot_called="$test_dir/reboot-called"
gpasswd_calls="$test_dir/gpasswd-calls"
usermod_calls="$test_dir/usermod-calls"
run() { # command STUB_GROUPS GUM_ANSWER DEFER(0|1)
rm -f "$reboot_flag" "$gum_called" "$reboot_called" "$gpasswd_calls" "$usermod_calls"
local defer_env=()
[[ ${4:-0} == 1 ]] && defer_env=(OMARCHY_DEFER_REBOOT=1)
env HOME="$home" USER="tester" STUB_GROUPS="$2" GUM_ANSWER="$3" \
GUM_CALLED="$gum_called" REBOOT_CALLED="$reboot_called" \
GPASSWD_CALLS="$gpasswd_calls" USERMOD_CALLS="$usermod_calls" \
PATH="$stub_bin:$ROOT/bin:$PATH" "${defer_env[@]}" \
bash "$ROOT/bin/$1" >/dev/null 2>&1
}
# Remove, interactive, reboot confirmed -> group removed, flag set, reboot fired.
run omarchy-remove-security-sudoless-docker "wheel input docker" 0 0
grep -q -- "-d tester docker" "$gpasswd_calls" || fail "remove drops the user from the docker group"
[[ -f $reboot_flag ]] || fail "remove flags a reboot"
[[ -f $reboot_called ]] || fail "remove reboots when the prompt is confirmed"
pass "remove drops the group, flags a reboot, and reboots on confirm"
# Remove, interactive, reboot declined -> flag set, but no reboot.
run omarchy-remove-security-sudoless-docker "wheel input docker" 1 0
[[ -f $reboot_flag ]] || fail "remove still flags a reboot when the prompt is declined"
[[ ! -f $reboot_called ]] || fail "remove does not reboot when the prompt is declined"
pass "remove leaves the reboot to the user when declined"
# Remove, deferred (migration/update) -> flag set, prompt never shown.
run omarchy-remove-security-sudoless-docker "wheel input docker" 0 1
[[ -f $reboot_flag ]] || fail "deferred remove still flags a reboot"
[[ ! -f $gum_called ]] || fail "deferred remove must not prompt to reboot"
[[ ! -f $reboot_called ]] || fail "deferred remove must not reboot"
pass "deferred remove flags the reboot without prompting"
# Remove, already out of the group -> no-op, nothing flagged.
run omarchy-remove-security-sudoless-docker "wheel input" 0 0
[[ ! -f $gpasswd_calls ]] || fail "remove is a no-op when the user is not in the docker group"
[[ ! -f $reboot_flag ]] || fail "remove does not flag a reboot when nothing changed"
pass "remove is a no-op when sudoless Docker is already off"
# Setup, enable confirmed then reboot confirmed -> group added, flag set, reboot.
run omarchy-setup-security-sudoless-docker "wheel input" 0 0
grep -q -- "-aG docker tester" "$usermod_calls" || fail "setup adds the user to the docker group"
[[ -f $reboot_flag ]] || fail "setup flags a reboot"
[[ -f $reboot_called ]] || fail "setup reboots when the prompt is confirmed"
pass "setup adds the group, flags a reboot, and reboots on confirm"
+1 -1
View File
@@ -129,7 +129,7 @@ pass "disk-space threshold includes the exact boundary"
rm -f "$snapshot_marker" "$gum_marker"
GUM_STATUS=0 TEST_AVAILABLE_BYTES=$((10 * 1024 * 1024 * 1024)) run_update >/dev/null
grep -q "confirm Continue with update?" "$gum_marker" ||
grep -q "confirm 继续更新?" "$gum_marker" ||
fail "interactive update with enough space uses the normal confirmation prompt"
[[ -f $snapshot_marker ]] || fail "accepting the normal confirmation starts the update"
pass "interactive update keeps the normal confirmation prompt when space is sufficient"
-30
View File
@@ -1,30 +0,0 @@
#!/bin/bash
source "$(dirname "${BASH_SOURCE[0]}")/base-test.sh"
require_command jq
require_command node
EXT_DIR="$ROOT/default/chromium/extensions/whatsapp-slim"
jq -e '
.content_scripts[0].js == ["system-theme.js"] and
.content_scripts[0].run_at == "document_start"
' "$EXT_DIR/manifest.json" >/dev/null || fail "WhatsApp enables system theming before startup"
mode=$(node - "$EXT_DIR/system-theme.js" <<'JS'
const fs = require("fs")
const values = new Map()
let reloaded = false
global.localStorage = {
getItem: key => values.get(key),
setItem: (key, value) => values.set(key, value),
}
global.location = { reload: () => { reloaded = true } }
eval(fs.readFileSync(process.argv[2], "utf8"))
process.stdout.write(`${values.get("system-theme-mode")}:${reloaded}`)
JS
)
[[ $mode == "true:true" ]] || fail "WhatsApp selects its system theme mode" "$mode"
pass "WhatsApp follows the system theme through its existing slim extension"